Additional role attributes && superuser review

Started by Stephen Frostabout 11 years ago186 messages
#1Stephen Frost
sfrost@snowman.net
1 attachment(s)

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

Attachments:

role-attributes.patchtext/x-diff; charset=us-asciiDownload
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+
  
#2Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Stephen Frost (#1)
Re: Additional role attributes && superuser review

On 10/15/14, 12:22 AM, Stephen Frost wrote:

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

It seems odd to me that this (presumably) supports PITR but not pg_dump*. I realize that most folks probably don't use pg_dump for actual backups, but I think it is very common to use it to produce schema-only (maybe with data from a few tables as well) dumps for developers. I've certainly wished I could offer that ability without going full-blown super-user.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Simon Riggs
simon@2ndQuadrant.com
In reply to: Stephen Frost (#1)
Re: Additional role attributes && superuser review

On 15 October 2014 06:22, Stephen Frost <sfrost@snowman.net> wrote:

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

Yes, but its more complex. As Jim says, you need to include pg_dump,
plus you need to include the streaming utilities, e.g. pg_basebackup.

LOGROTATE:
pg_rotate_logfile()

Do we need one just for that?

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

+1

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

RESERVED?

Perhaps we need a few others also - BASHFUL, HAPPY, GRUMPY etc

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Petr Jelinek
petr@2ndquadrant.com
In reply to: Stephen Frost (#1)
Re: Additional role attributes && superuser review

On 15/10/14 07:22, Stephen Frost wrote:

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()

As others have commented, I too think this should support pg_dump.

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.

Yeah it will, mainly because extensions can load modules and can have
untrusted functions, we might want to limit which extensions are
possible to create without being superuser.

tcop/utility.c
LOAD (load shared library)

This already somewhat handles non-superuser access. You can do LOAD as
normal user as long as the library is in $libdir/plugins directory so it
probably does not need separate role attribute (might be somehow useful
in combination with CREATE EXTENSION though).

commands/functioncmds.c
create untrusted-language functions

I often needed more granularity there (plproxy).

commands/functioncmds.c
execute DO blocks with untrusted languages

I am not sure if this is significantly different from untrusted-language
functions.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Stephen Frost
sfrost@snowman.net
In reply to: Petr Jelinek (#4)
Re: Additional role attributes && superuser review

* Petr Jelinek (petr@2ndquadrant.com) wrote:

On 15/10/14 07:22, Stephen Frost wrote:

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()

As others have commented, I too think this should support pg_dump.

I'm uttly mystified as to what that *means*. Everyone asking for it is
great but until someone can define what "support pg_dump" means, there's
not much progress I can make towards it..

pg_dump doesn't require superuser rights today. If you're looking for a
role which allows a user to arbitrairly read all data, fine, but that's
a different consideration and would be a different role attribute, imv.
If you'd like the role attribute renamed to avoid confusion, I'm all for
that, just suggest something.

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.

Yeah it will, mainly because extensions can load modules and can
have untrusted functions, we might want to limit which extensions
are possible to create without being superuser.

The extension has to be available on the filesystem before it can be
created, of course. I'm not against providing some kind of whitelist or
similar which a superuser could control.. That's similar to how PLs
work wrt pltemplate, no?

tcop/utility.c
LOAD (load shared library)

This already somewhat handles non-superuser access. You can do LOAD
as normal user as long as the library is in $libdir/plugins
directory so it probably does not need separate role attribute
(might be somehow useful in combination with CREATE EXTENSION
though).

Ah, fair enough. Note that I wasn't suggesting this be changed, just
noting it in my overall review.

commands/functioncmds.c
create untrusted-language functions

I often needed more granularity there (plproxy).

Not sure what you're getting at..? Is there a level of 'granularity'
for this which would make it safe for non-superusers to create
untrusted-language functions? I would think that'd be handled mainly
through extensions, but certainly curious what others think.

commands/functioncmds.c
execute DO blocks with untrusted languages

I am not sure if this is significantly different from
untrusted-language functions.

Nope, just another case where we're doing a superuser() check.

Thanks!

Stephen

#6Stephen Frost
sfrost@snowman.net
In reply to: Simon Riggs (#3)
Re: Additional role attributes && superuser review

* Simon Riggs (simon@2ndQuadrant.com) wrote:

On 15 October 2014 06:22, Stephen Frost <sfrost@snowman.net> wrote:

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

Yes, but its more complex. As Jim says, you need to include pg_dump,
plus you need to include the streaming utilities, e.g. pg_basebackup.

I'd rather have more, simpler, role attributes than one 'catch-all'.

Once I understand what "include pg_dump" and "include pg_basebackup"
mean, I'd be happy to work on adding those.

LOGROTATE:
pg_rotate_logfile()

Do we need one just for that?

It didn't seem to "belong" to any others and it's currently limited to
superuser but useful for non-superusers, so I would argue 'yes'. Now,
another option (actually, for many of these...) would be to trust in our
authorization system (GRANT EXECUTE) and remove the explicit superuser
check inside the functions, revoke EXECUTE from public, and tell users
to GRANT EXECUTE to the roles which should be allowed.

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

+1

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

RESERVED?

Seems reasonable, but do we need another GUC for how many connections
are reserved for 'RESERVED' roles? Or are we happy to allow those with
the RESERVED role attribute to contend for the same slots as superusers?

For my 2c- I'm happy to continue to have just one 'pool' of reserved
connections and just allow roles with RESERVED to connect using those
slots also.

Perhaps we need a few others also - BASHFUL, HAPPY, GRUMPY etc

Hah. :)

There was a suggestion raised (by Robert, I believe) that we store these
additional role attributes as a bitmask instead of individual columns.
I'm fine with either way, but it'd be a backwards-incompatible change
for anyone who looks at pg_authid. This would be across a major version
change, of course, so we are certainly within rights to do so, but I'm
also not sure how much we need to worry about a few bytes per pg_authid
row; we still have other issues if we want to try and support millions
of roles, starting with the inability to partition catalogs..

Thanks!

Stephen

#7Stephen Frost
sfrost@snowman.net
In reply to: Jim Nasby (#2)
Re: Additional role attributes && superuser review

Jim,

* Jim Nasby (Jim.Nasby@BlueTreble.com) wrote:

On 10/15/14, 12:22 AM, Stephen Frost wrote:

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

It seems odd to me that this (presumably) supports PITR but not pg_dump*. I realize that most folks probably don't use pg_dump for actual backups, but I think it is very common to use it to produce schema-only (maybe with data from a few tables as well) dumps for developers. I've certainly wished I could offer that ability without going full-blown super-user.

Can you clarify what you mean by "supports PITR but not pg_dump"?

The role attribute specifically allows a user to execute those
functions. Further, yes, this capability could be given to a role which
is used by, say, barman, to backup the database, or by other backup
solutions which do filesystem backups, but what do you mean by "does not
support pg_dump"?

What I think you're getting at here is a role attribute which can read
all data, which could certainly be done (as, say, a "READONLY"
attribute), but I view that a bit differently, as it could be used for
auditing and other purposes besides just non-superuser pg_dump support.

Thanks!

Stephen

#8Magnus Hagander
magnus@hagander.net
In reply to: Stephen Frost (#6)
Re: Additional role attributes && superuser review

On Oct 16, 2014 1:59 PM, "Stephen Frost" <sfrost@snowman.net> wrote:

* Simon Riggs (simon@2ndQuadrant.com) wrote:

On 15 October 2014 06:22, Stephen Frost <sfrost@snowman.net> wrote:

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

Yes, but its more complex. As Jim says, you need to include pg_dump,
plus you need to include the streaming utilities, e.g. pg_basebackup.

I'd rather have more, simpler, role attributes than one 'catch-all'.

Once I understand what "include pg_dump" and "include pg_basebackup"
mean, I'd be happy to work on adding those.

Include pg_basebackup would mean the replication protocol methods for base
backup and streaming. Which is already covered by the REPLICATION flag.

But in think it's somewhat useful to separate these. Being able to execute
pg_stop_backup allows you to break somebody else's backup currently
running, which pg_basebackup is safe against. So being able to call those
functions is clearly a higher privilege than being able to use
pg_basebackup.

/Magnus

#9Petr Jelinek
petr@2ndquadrant.com
In reply to: Stephen Frost (#5)
Re: Additional role attributes && superuser review

On 16/10/14 13:44, Stephen Frost wrote:

* Petr Jelinek (petr@2ndquadrant.com) wrote:

On 15/10/14 07:22, Stephen Frost wrote:

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()

As others have commented, I too think this should support pg_dump.

I'm uttly mystified as to what that *means*. Everyone asking for it is
great but until someone can define what "support pg_dump" means, there's
not much progress I can make towards it..

The explanation you wrote to Jim makes sense, plus given the comment
from Magnus about REPLICATION flag I guess I am happy enough with it (I
might have liked to have REPLICATION flag to somehow be part of BACKUP
flag but that's very minor thing).

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.

Yeah it will, mainly because extensions can load modules and can
have untrusted functions, we might want to limit which extensions
are possible to create without being superuser.

The extension has to be available on the filesystem before it can be
created, of course. I'm not against providing some kind of whitelist or
similar which a superuser could control.. That's similar to how PLs
work wrt pltemplate, no?

Makes sense, there is actually extension called pgextwlist which does this.

commands/functioncmds.c
create untrusted-language functions

I often needed more granularity there (plproxy).

Not sure what you're getting at..? Is there a level of 'granularity'
for this which would make it safe for non-superusers to create
untrusted-language functions? I would think that'd be handled mainly
through extensions, but certainly curious what others think.

I've been in situation where I wanted to give access to *some* untrusted
languages to non-superuser (as not every untrusted language can do same
kind of damage) and had to solve it via scripting outside of pg.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Simon Riggs
simon@2ndQuadrant.com
In reply to: Stephen Frost (#6)
Re: Additional role attributes && superuser review

On 16 October 2014 12:59, Stephen Frost <sfrost@snowman.net> wrote:

LOGROTATE:
pg_rotate_logfile()

Do we need one just for that?

It didn't seem to "belong" to any others and it's currently limited to
superuser but useful for non-superusers, so I would argue 'yes'. Now,
another option (actually, for many of these...) would be to trust in our
authorization system (GRANT EXECUTE) and remove the explicit superuser
check inside the functions, revoke EXECUTE from public, and tell users
to GRANT EXECUTE to the roles which should be allowed.

Seems like OPERATOR would be a general description that could be
useful elsewhere.

There was a suggestion raised (by Robert, I believe) that we store these
additional role attributes as a bitmask instead of individual columns.
I'm fine with either way, but it'd be a backwards-incompatible change
for anyone who looks at pg_authid. This would be across a major version
change, of course, so we are certainly within rights to do so, but I'm
also not sure how much we need to worry about a few bytes per pg_authid
row; we still have other issues if we want to try and support millions
of roles, starting with the inability to partition catalogs..

I assumed that was an internal change for fast access.

An array of role attributes would be extensible and more databasey.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Stephen Frost
sfrost@snowman.net
In reply to: Magnus Hagander (#8)
Re: Additional role attributes && superuser review

* Magnus Hagander (magnus@hagander.net) wrote:

On Oct 16, 2014 1:59 PM, "Stephen Frost" <sfrost@snowman.net> wrote:

Once I understand what "include pg_dump" and "include pg_basebackup"
mean, I'd be happy to work on adding those.

Include pg_basebackup would mean the replication protocol methods for base
backup and streaming. Which is already covered by the REPLICATION flag.

Well, right. I had the impression there was some distinction that I
just wasn't getting.

But in think it's somewhat useful to separate these. Being able to execute
pg_stop_backup allows you to break somebody else's backup currently
running, which pg_basebackup is safe against. So being able to call those
functions is clearly a higher privilege than being able to use
pg_basebackup.

Agreed.

Thanks!

Stephen

#12Stephen Frost
sfrost@snowman.net
In reply to: Petr Jelinek (#9)
Re: Additional role attributes && superuser review

* Petr Jelinek (petr@2ndquadrant.com) wrote:

The explanation you wrote to Jim makes sense, plus given the comment
from Magnus about REPLICATION flag I guess I am happy enough with it
(I might have liked to have REPLICATION flag to somehow be part of
BACKUP flag but that's very minor thing).

k. :)

The extension has to be available on the filesystem before it can be
created, of course. I'm not against providing some kind of whitelist or
similar which a superuser could control.. That's similar to how PLs
work wrt pltemplate, no?

Makes sense, there is actually extension called pgextwlist which does this.

Yeah. Not sure if that should only exist as an extension, but that's
really a conversation for a different thread.

Not sure what you're getting at..? Is there a level of 'granularity'
for this which would make it safe for non-superusers to create
untrusted-language functions? I would think that'd be handled mainly
through extensions, but certainly curious what others think.

I've been in situation where I wanted to give access to *some*
untrusted languages to non-superuser (as not every untrusted
language can do same kind of damage) and had to solve it via
scripting outside of pg.

Really curious what untrusted language you're referring to which isn't
as risky as others..? Any kind of filesystem or network access, or the
ability to run external commands, is dangerous to give non-superusers.

Perhaps more to the point- what would the more granular solution look
like..? Though, this is still getting a bit off-topic for this thread.

Thanks!

Stephen

#13Stephen Frost
sfrost@snowman.net
In reply to: Simon Riggs (#10)
Re: Additional role attributes && superuser review

* Simon Riggs (simon@2ndQuadrant.com) wrote:

On 16 October 2014 12:59, Stephen Frost <sfrost@snowman.net> wrote:

LOGROTATE:
pg_rotate_logfile()

Do we need one just for that?

It didn't seem to "belong" to any others and it's currently limited to
superuser but useful for non-superusers, so I would argue 'yes'. Now,
another option (actually, for many of these...) would be to trust in our
authorization system (GRANT EXECUTE) and remove the explicit superuser
check inside the functions, revoke EXECUTE from public, and tell users
to GRANT EXECUTE to the roles which should be allowed.

Seems like OPERATOR would be a general description that could be
useful elsewhere.

Ok.. but what else? Are there specific functions which aren't covered
by these role attributes which should be and could fall into this
category? I'm not against the idea of an 'operator' role, but I don't
like the idea that it means 'logrotate, until we figure out some other
thing which makes sense to put into this category'. For one thing, this
approach would mean that future version which add more rights to the
'operator' role attribute would mean that upgrades are granting rights
which didn't previously exist..

There was a suggestion raised (by Robert, I believe) that we store these
additional role attributes as a bitmask instead of individual columns.
I'm fine with either way, but it'd be a backwards-incompatible change
for anyone who looks at pg_authid. This would be across a major version
change, of course, so we are certainly within rights to do so, but I'm
also not sure how much we need to worry about a few bytes per pg_authid
row; we still have other issues if we want to try and support millions
of roles, starting with the inability to partition catalogs..

I assumed that was an internal change for fast access.

We could make it that way by turning pg_authid into a view and using a
new catalog table for roles instead (similar to what we did with
pg_user ages ago when we added roles), but that feels like overkill to
me.

An array of role attributes would be extensible and more databasey.

Hrm. Agreed, and it would save a bit of space for the common case where
the user hasn't got any of these attributes, though it wouldn't be as
efficient as a bitmap.

For my part, I'm not really wedded to any particular catalog
representation. Having reviewed the various superuser checks, I think
there's a few more role attributes which could/should be added beyond
the ones listed, but I don't think we'll ever get to 64 of them, so a
single int64 would work if we want the most compact solution.

Thanks!

Stephen

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#5)
Re: Additional role attributes && superuser review

Stephen Frost <sfrost@snowman.net> writes:

* Petr Jelinek (petr@2ndquadrant.com) wrote:

Yeah it will, mainly because extensions can load modules and can
have untrusted functions, we might want to limit which extensions
are possible to create without being superuser.

The extension has to be available on the filesystem before it can be
created, of course. I'm not against providing some kind of whitelist or
similar which a superuser could control.. That's similar to how PLs
work wrt pltemplate, no?

The existing behavior is "you can create an extension if you can execute
all the commands contained in its script". I'm not sure that messing
with that rule is a good idea; in any case it seems well out of scope
for this patch.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#14)
Re: Additional role attributes && superuser review

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Petr Jelinek (petr@2ndquadrant.com) wrote:

Yeah it will, mainly because extensions can load modules and can
have untrusted functions, we might want to limit which extensions
are possible to create without being superuser.

The extension has to be available on the filesystem before it can be
created, of course. I'm not against providing some kind of whitelist or
similar which a superuser could control.. That's similar to how PLs
work wrt pltemplate, no?

The existing behavior is "you can create an extension if you can execute
all the commands contained in its script". I'm not sure that messing
with that rule is a good idea; in any case it seems well out of scope
for this patch.

Right, that's the normal rule. I still like the idea of letting
non-superusers create "safe" extensions, but I completely agree- beyond
the scope of this patch (as I noted in my initial post).

Thanks!

Stephen

#16Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#5)
Re: Additional role attributes && superuser review

Stephen Frost wrote:

* Petr Jelinek (petr@2ndquadrant.com) wrote:

On 15/10/14 07:22, Stephen Frost wrote:

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()

As others have commented, I too think this should support pg_dump.

I'm uttly mystified as to what that *means*. Everyone asking for it is
great but until someone can define what "support pg_dump" means, there's
not much progress I can make towards it..

To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say.

pg_dump doesn't require superuser rights today. If you're looking for a
role which allows a user to arbitrairly read all data, fine, but that's
a different consideration and would be a different role attribute, imv.
If you'd like the role attribute renamed to avoid confusion, I'm all for
that, just suggest something.

There.

(Along the same lines, perhaps the log rotate thingy that's being
mentioned elsewhere could be LOG_OPERATOR instead of just OPERATOR, to
avoid privilege "upgrades" as later releases include more capabilities
to the hypothetical OPERATOR capability.)

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#16)
Re: Additional role attributes && superuser review

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

* Petr Jelinek (petr@2ndquadrant.com) wrote:

On 15/10/14 07:22, Stephen Frost wrote:

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()

As others have commented, I too think this should support pg_dump.

I'm uttly mystified as to what that *means*. Everyone asking for it is
great but until someone can define what "support pg_dump" means, there's
not much progress I can make towards it..

To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say.

Ok. Not sure that I see 'XLOG_OPERATOR' as making sense for
'pg_start_backup' though, and I see the need to support pg_dump'ing the
whole database as a non-superuser being more like a 'READONLY' kind of
role.

We've actually already looked at the notion of a 'READONLY' or 'READALL'
role attribute and, well, it's quite simple to implement.. We're
talking about a few lines of code to implicitly allow 'USAGE' on all
schemas, plus a minor change in ExecCheckRTEPerms to always (or perhaps
*only*) include SELECT rights. As there's so much interest in that
capability, perhaps we should add it..

One of the big question is- do we take steps to try and prevent a role
with this attribute from being able to modify the database in any way?
Or would this role attribute only ever increase your rights?

pg_dump doesn't require superuser rights today. If you're looking for a
role which allows a user to arbitrairly read all data, fine, but that's
a different consideration and would be a different role attribute, imv.
If you'd like the role attribute renamed to avoid confusion, I'm all for
that, just suggest something.

There.

Fair enough, just don't like the specific suggestions. :) In the docs,
we talk about pg_start_backup being for 'on-line' backups, perhaps we
use ONLINE_BACKUP ? Or PITR_BACKUP ?

(Along the same lines, perhaps the log rotate thingy that's being
mentioned elsewhere could be LOG_OPERATOR instead of just OPERATOR, to
avoid privilege "upgrades" as later releases include more capabilities
to the hypothetical OPERATOR capability.)

I'd be fine calling it LOG_OPERATOR instead, sure.

Thanks!

Stephen

#18Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#16)
Re: Additional role attributes && superuser review

On Thu, Oct 16, 2014 at 11:24 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Stephen Frost wrote:

* Petr Jelinek (petr@2ndquadrant.com) wrote:

On 15/10/14 07:22, Stephen Frost wrote:

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()

As others have commented, I too think this should support pg_dump.

I'm uttly mystified as to what that *means*. Everyone asking for it is
great but until someone can define what "support pg_dump" means, there's
not much progress I can make towards it..

To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say.

I'm a little nervous that we're going to end up with a whole bunch of
things with names like X_control, Y_operator, and Z_admin, which I
think is particularly bad if we end up with a mix of styles and also
bad (though less so) if we end up just tacking the word "operator"
onto the end of everything.

I'd suggest calling these capabilities, and allow:

GRANT CAPABILITY whatever TO somebody;

...but keep extraneous words like "control" or "operator" out of the
capabilities names themselves. So just wal, xlog, logfile, etc.
rather than wal_operator, xlog_operator, logfile_operator and so on.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#18)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Oct 16, 2014 at 11:24 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say.

I'm a little nervous that we're going to end up with a whole bunch of
things with names like X_control, Y_operator, and Z_admin, which I
think is particularly bad if we end up with a mix of styles and also
bad (though less so) if we end up just tacking the word "operator"
onto the end of everything.

Yeah, that's certainly a good point.

I'd suggest calling these capabilities, and allow:

GRANT CAPABILITY whatever TO somebody;

So, we went back to just role attributes to avoid the keyword issue..
The above would require making 'CAPABILITY' a reserved word, and there
really isn't a 'good' already-reserved word we can use there that I
found.

Also, role attributes aren't inheirited nor is there an 'ADMIN' option
for them as there is for GRANT- both of which I feel are correct for
these capabilities. Or, to say it another way, I don't think these
should have an 'ADMIN' option and I don't think they need to be
inheirited through role membership the way granted privileges are.

We could still use 'GRANT <keyword> whatever TO somebody;' without the
admin opton and without inheiritance, but I think it'd just be
confusing for users who are familiar with how GRANT works already.

Thanks!

Stephen

#20Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#19)
Re: Additional role attributes && superuser review

On Thu, Oct 16, 2014 at 2:59 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Oct 16, 2014 at 11:24 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say.

I'm a little nervous that we're going to end up with a whole bunch of
things with names like X_control, Y_operator, and Z_admin, which I
think is particularly bad if we end up with a mix of styles and also
bad (though less so) if we end up just tacking the word "operator"
onto the end of everything.

Yeah, that's certainly a good point.

I'd suggest calling these capabilities, and allow:

GRANT CAPABILITY whatever TO somebody;

So, we went back to just role attributes to avoid the keyword issue..
The above would require making 'CAPABILITY' a reserved word, and there
really isn't a 'good' already-reserved word we can use there that I
found.

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#20)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

That works for me- would we change the existing role attributes to be
configurable this way and change everything over to using an int64 in
the catalog? Unless I'm having trouble counting, I think that would
actually result in the pg_authid catalog not changing in size at all
while giving us the ability to add these capabilities and something like
50 others if we had cause to.

Thanks,

Stephen

#22Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#21)
Re: Additional role attributes && superuser review

On Thu, Oct 16, 2014 at 3:09 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

That works for me- would we change the existing role attributes to be
configurable this way and change everything over to using an int64 in
the catalog? Unless I'm having trouble counting, I think that would
actually result in the pg_authid catalog not changing in size at all
while giving us the ability to add these capabilities and something like
50 others if we had cause to.

I definitely think we should support the new syntax for the existing
attributes. I could go either way on whether to change the catalog
storage for the existing attributes. Some people might prefer to
avoid the backward compatibility break, and I can see that argument.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#22)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Oct 16, 2014 at 3:09 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

That works for me- would we change the existing role attributes to be
configurable this way and change everything over to using an int64 in
the catalog? Unless I'm having trouble counting, I think that would
actually result in the pg_authid catalog not changing in size at all
while giving us the ability to add these capabilities and something like
50 others if we had cause to.

I definitely think we should support the new syntax for the existing
attributes.

Ok.

I could go either way on whether to change the catalog
storage for the existing attributes. Some people might prefer to
avoid the backward compatibility break, and I can see that argument.

There's really two issues when it comes to backwards compatibility here-
the catalog representation and the syntax.

My feeling is basically this- either we make a clean break to the new
syntax and catalog representation, or we just use the same approach the
existing attriubtes use. Long term, I think your proposed syntax and an
int64 representation is better but it'll mean a lot of client code that
has to change. I don't really like the idea of changing the syntax but
not the representation, nor am I thrilled with the idea of supporting
both syntaxes, and changing the syntax without changing the
representation just doesn't make sense to me as I think we'd end up
wanting to change it later, making clients have to update their code
twice.

Thanks!

Stephen

#24Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#20)
Re: Additional role attributes && superuser review

On 16 October 2014 20:04, Robert Haas <robertmhaas@gmail.com> wrote:

I'd suggest calling these capabilities, and allow:

GRANT CAPABILITY whatever TO somebody;

So, we went back to just role attributes to avoid the keyword issue..
The above would require making 'CAPABILITY' a reserved word, and there
really isn't a 'good' already-reserved word we can use there that I
found.

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

I thought you had it right first time. It is mighty annoying that some
privileges are GRANTed and others ALTER ROLEd.

And we did agree earlier to call these capabilities.

How about

GRANT EXECUTE [PRIVILEGES] ON CAPABILITY foo TO bar;

That is similar to granting execution privs on a function. And I think
gets round the keyword issue?

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Stephen Frost
sfrost@snowman.net
In reply to: Simon Riggs (#24)
Re: Additional role attributes && superuser review

* Simon Riggs (simon@2ndQuadrant.com) wrote:

On 16 October 2014 20:04, Robert Haas <robertmhaas@gmail.com> wrote:

GRANT CAPABILITY whatever TO somebody;

So, we went back to just role attributes to avoid the keyword issue..
The above would require making 'CAPABILITY' a reserved word, and there
really isn't a 'good' already-reserved word we can use there that I
found.

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

I thought you had it right first time. It is mighty annoying that some
privileges are GRANTed and others ALTER ROLEd.

Yeah- but there's a material difference in the two, as I tried to
outline previously..

How about

GRANT EXECUTE [PRIVILEGES] ON CAPABILITY foo TO bar;

That is similar to granting execution privs on a function. And I think
gets round the keyword issue?

No, it doesn't.. EXECUTE isn't reserved at all.

Thanks,

Stephen

#26Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Stephen Frost (#17)
Re: Additional role attributes && superuser review

On 10/16/14, 10:47 AM, Stephen Frost wrote:

As others have commented, I too think this should support pg_dump.

I'm uttly mystified as to what that*means*. Everyone asking for it is
great but until someone can define what "support pg_dump" means, there's
not much progress I can make towards it..

To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say.

Ok. Not sure that I see 'XLOG_OPERATOR' as making sense for
'pg_start_backup' though, and I see the need to support pg_dump'ing the
whole database as a non-superuser being more like a 'READONLY' kind of
role.

We've actually already looked at the notion of a 'READONLY' or 'READALL'
role attribute and, well, it's quite simple to implement.. We're
talking about a few lines of code to implicitly allow 'USAGE' on all
schemas, plus a minor change in ExecCheckRTEPerms to always (or perhaps
*only*) include SELECT rights. As there's so much interest in that
capability, perhaps we should add it..

One of the big question is- do we take steps to try and prevent a role
with this attribute from being able to modify the database in any way?
Or would this role attribute only ever increase your rights?

Let me address the pg_dump case first, because it's simpler. I want a way to allow certain roles to successfully run pg_dump without being superuser and without having to manually try and maintain a magic read-all role. Note that pg_dump might (today or in the future) need more than just read-all so it's probably wise to treat the two features (pg_dump vs a plain read-all) as separate.

For PITR, I see 3 different needs:

1) The ability for someone to start a backup, and if needed cancel that backup
2) The ability to manage running backups/archiving
3) The ability to actually setup/modify PITR infrastructure

#2 is probably a weak case that may not be needed; I include it because someone mentioned stopping a backup that someone else started.

I think #3 should just require superuser.

#1 is what you'd want a more junior person to handle. "Bob needs a snapshot of cluster A". This role needs to be able to run everything you need to get that backup started, monitor it, and cancel if needed.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#23)
Re: Additional role attributes && superuser review

On Thu, Oct 16, 2014 at 3:34 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Oct 16, 2014 at 3:09 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

Ah, good point. Using ALTER ROLE is better. Maybe we should do ALTER
ROLE .. [ ADD | DROP ] CAPABILITY x. That would still require making
CAPABILITY a keyword, but it could be unreserved.

That works for me- would we change the existing role attributes to be
configurable this way and change everything over to using an int64 in
the catalog? Unless I'm having trouble counting, I think that would
actually result in the pg_authid catalog not changing in size at all
while giving us the ability to add these capabilities and something like
50 others if we had cause to.

I definitely think we should support the new syntax for the existing
attributes.

Ok.

I could go either way on whether to change the catalog
storage for the existing attributes. Some people might prefer to
avoid the backward compatibility break, and I can see that argument.

There's really two issues when it comes to backwards compatibility here-
the catalog representation and the syntax.

My feeling is basically this- either we make a clean break to the new
syntax and catalog representation, or we just use the same approach the
existing attriubtes use. Long term, I think your proposed syntax and an
int64 representation is better but it'll mean a lot of client code that
has to change. I don't really like the idea of changing the syntax but
not the representation, nor am I thrilled with the idea of supporting
both syntaxes, and changing the syntax without changing the
representation just doesn't make sense to me as I think we'd end up
wanting to change it later, making clients have to update their code
twice.

I don't see any reason why it has to be both or neither.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#27)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Oct 16, 2014 at 3:34 PM, Stephen Frost <sfrost@snowman.net> wrote:

My feeling is basically this- either we make a clean break to the new
syntax and catalog representation, or we just use the same approach the
existing attriubtes use. Long term, I think your proposed syntax and an
int64 representation is better but it'll mean a lot of client code that
has to change. I don't really like the idea of changing the syntax but
not the representation, nor am I thrilled with the idea of supporting
both syntaxes, and changing the syntax without changing the
representation just doesn't make sense to me as I think we'd end up
wanting to change it later, making clients have to update their code
twice.

I don't see any reason why it has to be both or neither.

My reasoning on that is that either breaks existing clients and so
they'll have to update and if we're going to force that then we might as
well go whole-hog and get it over with once.

If my assumption is incorrect and we don't think changes to the catalog
representation will break existing clients then I withdraw the argument
that they should be done together.

For the syntax piece of it, my feeling is that all these role attributes
should be handled the same way. There's three ways to address that-
support them using the existing syntax, change to a new syntax and only
support that, or have two different syntaxes. I don't like the idea
that these new attributes will be supported with one syntax but the old
attributes would be supported with a different syntax.

If we think it's too much to change in one release, I could see changing
the catalog representation and then providing the new syntax while
supporting the old syntax as deprecated, but we do have a pretty bad
history of such changes and any maintained client should be getting
updated for the new role attributes anyway..

Thanks!

Stephen

#29Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Robert Haas (#27)
Re: Additional role attributes && superuser review

Robert Haas wrote:

On Thu, Oct 16, 2014 at 3:34 PM, Stephen Frost <sfrost@snowman.net> wrote:

My feeling is basically this- either we make a clean break to the new
syntax and catalog representation, or we just use the same approach the
existing attriubtes use. Long term, I think your proposed syntax and an
int64 representation is better but it'll mean a lot of client code that
has to change. I don't really like the idea of changing the syntax but
not the representation, nor am I thrilled with the idea of supporting
both syntaxes, and changing the syntax without changing the
representation just doesn't make sense to me as I think we'd end up
wanting to change it later, making clients have to update their code
twice.

I don't see any reason why it has to be both or neither.

I was thinking we would change the catalogs and implement the new syntax
for new and old settings, but also keep the old syntax working as a
backward compatibility measure. I don't see what's so terrible about
continuing to support the old syntax; we do that in COPY and EXPLAIN,
for example.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Simon Riggs
simon@2ndQuadrant.com
In reply to: Stephen Frost (#25)
Re: Additional role attributes && superuser review

On 16 October 2014 20:37, Stephen Frost <sfrost@snowman.net> wrote:

How about

GRANT EXECUTE [PRIVILEGES] ON CAPABILITY foo TO bar;

That is similar to granting execution privs on a function. And I think
gets round the keyword issue?

No, it doesn't.. EXECUTE isn't reserved at all.

Yet GRANT EXECUTE is already valid syntax, so that should work.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Stephen Frost
sfrost@snowman.net
In reply to: Simon Riggs (#30)
Re: Additional role attributes && superuser review

* Simon Riggs (simon@2ndQuadrant.com) wrote:

On 16 October 2014 20:37, Stephen Frost <sfrost@snowman.net> wrote:

How about

GRANT EXECUTE [PRIVILEGES] ON CAPABILITY foo TO bar;

That is similar to granting execution privs on a function. And I think
gets round the keyword issue?

No, it doesn't.. EXECUTE isn't reserved at all.

Yet GRANT EXECUTE is already valid syntax, so that should work.

Yeah, sorry, the issue with the above is that the "ON CAPABILITY" would
mean CAPABILITY needs to be reserved as otherwise we don't know if it's
a function or something else.

The keyword issue is with

GRANT <something> TO <role>;

As <something> could be a role.

Not sure offhand if

GRANT EXECUTE PRIVILEGES ON CAPABILITY foo TO bar;

would work.. In general, I'm not anxious to get involved in the
SQL-specified GRANT syntax though unless there's really good reason to.

Also, these aren't like normally granted privileges which can have an
ADMIN option and which are inheirited through role membership..

Thanks,

Stephen

#32Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#29)
Re: Additional role attributes && superuser review

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Robert Haas wrote:

On Thu, Oct 16, 2014 at 3:34 PM, Stephen Frost <sfrost@snowman.net> wrote:

My feeling is basically this- either we make a clean break to the new
syntax and catalog representation, or we just use the same approach the
existing attriubtes use. Long term, I think your proposed syntax and an
int64 representation is better but it'll mean a lot of client code that
has to change. I don't really like the idea of changing the syntax but
not the representation, nor am I thrilled with the idea of supporting
both syntaxes, and changing the syntax without changing the
representation just doesn't make sense to me as I think we'd end up
wanting to change it later, making clients have to update their code
twice.

I don't see any reason why it has to be both or neither.

I was thinking we would change the catalogs and implement the new syntax
for new and old settings, but also keep the old syntax working as a
backward compatibility measure. I don't see what's so terrible about
continuing to support the old syntax; we do that in COPY and EXPLAIN,
for example.

It just complicates things and I'm not sure there's much benefit to it.
Clients are going to need to be updated to support the new attributes
anyway, and if the new attributes can only be used through the new
syntax, well, I don't know why they'd want to deal with the old syntax
too.

That said, I don't feel very strongly about that position, so if you and
Robert (and others on the thread) agree that's the right approach then
I'll see about getting it done.

Thanks!

Stephen

#33Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Stephen Frost (#32)
Re: Additional role attributes && superuser review

All,

That said, I don't feel very strongly about that position, so if you and
Robert (and others on the thread) agree that's the right approach then
I'll see about getting it done.

We haven't reached consensus on this one yet and I didn't want it to fall
too far off the radar.

Here is what I summarize as the current state of the discussion:

1. Syntax:

ALTER ROLE <role> { ADD | DROP } CAPABILITY <capability>

* I think this is the most straight forward approach as it still close to
the current syntax.
* Perhaps keeping the current syntax around as deprecated to be removed in
a scheduled future version. (provide a "deprecated" message to the user?)

or

GRANT EXECUTE PRIVILEGES ON <capability> TO <role>

* Though, this will be tricky since EXECUTE is not reserved and the
currently state of GRANT in the grammar would require either refactoring or
making it RESERVED... neither I think are acceptable at this point in time
for obvious reasons.

2. Catalog Representation:

Condense all attributes in pg_authid to single int64 column and create
bitmasks accordingly.

Obviously there is some concern for upgrading and whether to do both at
once or to do them incrementally. IMHO, I think if the changes are going
to be made, then we should go ahead and do them at the same time. Though,
would it be beneficial to separate in to two distinct patches?

-Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#34Stephen Frost
sfrost@snowman.net
In reply to: Adam Brightwell (#33)
Re: Additional role attributes && superuser review

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

That said, I don't feel very strongly about that position, so if you and
Robert (and others on the thread) agree that's the right approach then
I'll see about getting it done.

Thanks for trying to make progress on this.

We haven't reached consensus on this one yet and I didn't want it to fall
too far off the radar.

Here is what I summarize as the current state of the discussion:

For my 2c- I don't think we'd be able to drop the old syntax and
therefore I'm not sure that I really see the point in adding new syntax.
I don't hold that position strongly, but I don't like the idea that
we're just going to be sitting on our thumbs because we can't agree on
the color of the bike shed.

If there is agreement to move forward with using the syntax below,
great, that can be implemented in relatively short order. If there
isn't, then let's move forward with the existing syntax and change it
later, if there is agreement to do so at some point in the future.

I don't like the idea of tying it into GRANT. GRANT is SQL-spec
defined, quite overloaded already, and role attributes don't act like
GRANTs anyway (there's no ADMIN option and they aren't inheirited).

2. Catalog Representation:

Condense all attributes in pg_authid to single int64 column and create
bitmasks accordingly.

I'd suggest we do this regardless and the only question is if we feel
there is need to provide a view to split them back out again or not.
I'm inclined to say 'no' to another view and instead suggest we provide
SQL-level "has_whatever_privilege" for these which match the existing C
functions, update our clients to use those, and encourage others to do
the same. Probably also helpful would be a single function that returns
a simple list of the non-default role attributes which a user has and
we'd simplify psql's describeRoles by having it use that instead.

Obviously there is some concern for upgrading and whether to do both at
once or to do them incrementally. IMHO, I think if the changes are going
to be made, then we should go ahead and do them at the same time. Though,
would it be beneficial to separate in to two distinct patches?

I agree that doing these changes at the same time makes sense, if we can
get agreement on what exactly to do. I've shared my thoughts, but we
really need input from others, ideally of the form of "I prefer X over
Y" rather than "well, we could do X or Y".

Thanks!

Stephen

#35Robert Haas
robertmhaas@gmail.com
In reply to: Adam Brightwell (#33)
Re: Additional role attributes && superuser review

On Mon, Nov 3, 2014 at 11:44 AM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

That said, I don't feel very strongly about that position, so if you and
Robert (and others on the thread) agree that's the right approach then
I'll see about getting it done.

We haven't reached consensus on this one yet and I didn't want it to fall
too far off the radar.

Here is what I summarize as the current state of the discussion:

1. Syntax:

ALTER ROLE <role> { ADD | DROP } CAPABILITY <capability>

Seems reasonable.

* Perhaps keeping the current syntax around as deprecated to be removed in a
scheduled future version. (provide a "deprecated" message to the user?)

Yeah, I don't think we should remove the existing syntax because a lot
of people are used to it. We still have some very old COPY syntax
around for backward-compatibility, and it's not hurting us, either.
At the same time, I think recasting the existing capability-like
things as capabilities per se is a good idea, because otherwise we've
got this old stuff that is randomly different for no especially good
reason.

GRANT EXECUTE PRIVILEGES ON <capability> TO <role>

Yuck.

The only other approach I can think of is have some magic, hard-coded
roles that implicitly have each capability, and then those can be
granted. e.g. have a role pg_unrestricted_copy or whatever with a
given OID, and then test has_privs_of_role() with that OID.

Condense all attributes in pg_authid to single int64 column and create
bitmasks accordingly.

Obviously there is some concern for upgrading and whether to do both at once
or to do them incrementally. IMHO, I think if the changes are going to be
made, then we should go ahead and do them at the same time. Though, would
it be beneficial to separate in to two distinct patches?

If we're going to change the catalog representation for the existing
capabilities, I'd recommend that the first patch change the catalog
representation and add the new syntax without adding any more
capabilities; and then the second and subsequent patches can add
additional capabilities.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Adam Brightwell (#33)
Re: Additional role attributes && superuser review

All,

2. Catalog Representation:

Condense all attributes in pg_authid to single int64 column and create
bitmasks accordingly.

I have been working on these changes and I was hoping for some
assistance/recommendations.

Currently, I am using int32 simply because int64 is causing some issues.
The issue is that genbki.pl is not able to associate it with the int8 type
as defined in pg_type.h. Therefore Schema_pg_authid in schemapg.h isn't
defined correctly. I've been digging and scratching my head on this one
but I have reached a point where I think it would be better just to ask.

Any thoughts or recommendations on how I should approach this?

-Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#37Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Adam Brightwell (#36)
1 attachment(s)
Re: Additional role attributes && superuser review

All,

Currently, I am using int32 simply because int64 is causing some issues.
The issue is that genbki.pl is not able to associate it with the int8
type as defined in pg_type.h. Therefore Schema_pg_authid in schemapg.h
isn't defined correctly. I've been digging and scratching my head on this
one but I have reached a point where I think it would be better just to ask.

Attached is a quite trivial patch that addresses the int64 (C) to int8
(SQL) mapping issue.

Further digging revealed that Catalog.pm wasn't accounting for int64
(thanks Stephen). Would it be better to include this change as a separate
patch (as attached) or would it be preferable to include with a larger role
attribute bitmask patch?

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

Attachments:

int64_catalog_atttype.patchtext/x-patch; charset=US-ASCII; name=int64_catalog_atttype.patchDownload
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
new file mode 100644
index eb91c53..523b379
*** a/src/backend/catalog/Catalog.pm
--- b/src/backend/catalog/Catalog.pm
*************** sub Catalogs
*** 33,38 ****
--- 33,39 ----
  	my %RENAME_ATTTYPE = (
  		'int16'         => 'int2',
  		'int32'         => 'int4',
+ 		'int64'         => 'int8',
  		'Oid'           => 'oid',
  		'NameData'      => 'name',
  		'TransactionId' => 'xid');
#38Tom Lane
tgl@sss.pgh.pa.us
In reply to: Adam Brightwell (#36)
Re: Additional role attributes && superuser review

Adam Brightwell <adam.brightwell@crunchydatasolutions.com> writes:

Currently, I am using int32 simply because int64 is causing some issues.
The issue is that genbki.pl is not able to associate it with the int8 type
as defined in pg_type.h. Therefore Schema_pg_authid in schemapg.h isn't
defined correctly. I've been digging and scratching my head on this one
but I have reached a point where I think it would be better just to ask.

Any thoughts or recommendations on how I should approach this?

Teach src/backend/catalog/Catalog.pm about it.

There once was a policy against using int64 in the system catalogs, which
is why it wasn't there already; but the reason for that policy is long
gone.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Andrew Dunstan
andrew@dunslane.net
In reply to: Adam Brightwell (#37)
Re: Additional role attributes && superuser review

On 11/18/2014 04:58 PM, Adam Brightwell wrote:

All,

Currently, I am using int32 simply because int64 is causing some
issues. The issue is that genbki.pl <http://genbki.pl&gt; is not
able to associate it with the int8 type as defined in pg_type.h.
Therefore Schema_pg_authid in schemapg.h isn't defined correctly.
I've been digging and scratching my head on this one but I have
reached a point where I think it would be better just to ask.

Attached is a quite trivial patch that addresses the int64 (C) to int8
(SQL) mapping issue.

Further digging revealed that Catalog.pm wasn't accounting for int64
(thanks Stephen). Would it be better to include this change as a
separate patch (as attached) or would it be preferable to include with
a larger role attribute bitmask patch?

I think we should just apply this now. As Tom said the reason for not
doing it is long gone.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Robert Haas (#35)
1 attachment(s)
Re: Additional role attributes && superuser review

All,

If we're going to change the catalog representation for the existing
capabilities, I'd recommend that the first patch change the catalog
representation and add the new syntax without adding any more
capabilities; and then the second and subsequent patches can add
additional capabilities.

Attached is an initial patch for review and discussion that takes a swing
at changing the catalog representation.

This patch includes:

* int64 (C) to int8 (SQL) mapping for genbki.
* replace all role attributes columns in pg_authid with single int64 column
named rolattr.
* update CreateRole and AlterRole to use rolattr.
* update all has_*_privilege functions to check rolattr.
* builtin SQL function 'has_role_attribute' that takes a role oid and text
name of the attribute as input and returns a boolean.

Items not currently addressed:

* New syntax - more discussion needs to occur around these potential
changes. Specifically, how would CREATE USER/ROLE be affected? I suppose
it is OK to keep it as WITH <attribute_or_capability>, though if ALTER ROLE
is modified to have ADD | DROP CAPABILITY for consistency would WITH
CAPABILITY <value,...>, make more sense for CREATE? At any rate, I think
there is obviously much more discussion that needs to be had.
* Documentation - want to gain feedback on implementation prior to making
changes.
* Update regression tests, rules test for system_views - want to gain
feedback on approach to handling pg_roles, etc. before updating.

Also, what would be the community preference on tracking these
attached/proposed changes? Should I create a separate entry in the next CF
for this item (seems prudent) or would it be preferred to keep it attached
to the current 'new attributes' CF entry?

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

Attachments:

role-attribute-bitmask-v1.patchtext/x-patch; charset=US-ASCII; name=role-attribute-bitmask-v1.patchDownload
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
new file mode 100644
index eb91c53..523b379
*** a/src/backend/catalog/Catalog.pm
--- b/src/backend/catalog/Catalog.pm
*************** sub Catalogs
*** 33,38 ****
--- 33,39 ----
  	my %RENAME_ATTTYPE = (
  		'int16'         => 'int2',
  		'int32'         => 'int4',
+ 		'int64'         => 'int8',
  		'Oid'           => 'oid',
  		'NameData'      => 'name',
  		'TransactionId' => 'xid');
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..93eb2e6
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** aclcheck_error_type(AclResult aclerr, Oi
*** 3423,3448 ****
  }
  
  
- /* Check if given user has rolcatupdate privilege according to pg_authid */
- static bool
- has_rolcatupdate(Oid roleid)
- {
- 	bool		rolcatupdate;
- 	HeapTuple	tuple;
- 
- 	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- 	if (!HeapTupleIsValid(tuple))
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("role with OID %u does not exist", roleid)));
- 
- 	rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate;
- 
- 	ReleaseSysCache(tuple);
- 
- 	return rolcatupdate;
- }
- 
  /*
   * Relay for the various pg_*_mask routines depending on object kind
   */
--- 3423,3428 ----
*************** 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
--- 3610,3616 ----
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!role_has_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
*************** pg_extension_ownercheck(Oid ext_oid, Oid
*** 5051,5056 ****
--- 5031,5058 ----
  }
  
  /*
+  * Check whether the specified role has a specific role attribute.
+  */
+ bool
+ role_has_attribute(Oid roleid, RoleAttr attribute)
+ {
+ 	RoleAttr	attributes;
+ 	HeapTuple	tuple;
+ 
+ 	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("role with OID %u does not exist", roleid)));
+ 
+ 	attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
+ 	ReleaseSysCache(tuple);
+ 
+ 	return ((attributes & attribute) > 0);
+ }
+ 
+ /*
   * Check whether specified role has CREATEROLE privilege (or is a superuser)
   *
   * Note: roles do not have owners per se; instead we use this test in
*************** pg_extension_ownercheck(Oid ext_oid, Oid
*** 5064,5102 ****
  bool
  has_createrole_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))->rolcreaterole;
! 		ReleaseSysCache(utup);
! 	}
! 	return result;
  }
  
  bool
  has_bypassrls_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))->rolbypassrls;
! 		ReleaseSysCache(utup);
! 	}
! 	return result;
  }
  
  /*
--- 5066,5089 ----
  bool
  has_createrole_privilege(Oid roleid)
  {
  	/* Superusers bypass all permission checking. */
  	if (superuser_arg(roleid))
  		return true;
  
! 	return role_has_attribute(roleid, ROLE_ATTR_CREATEROLE);
  }
  
+ /*
+  * Check whether specified role has BYPASSRLS privilege.
+  */
  bool
  has_bypassrls_privilege(Oid roleid)
  {
  	/* Superusers bypass all permission checking. */
  	if (superuser_arg(roleid))
  		return true;
  
! 	return role_has_attribute(roleid, ROLE_ATTR_BYPASSRLS);
  }
  
  /*
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
new file mode 100644
index a036c62..6904716
*** a/src/backend/catalog/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
*************** CREATE VIEW user_mapping_options AS
*** 2884,2890 ****
             CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
             CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
                         OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
!                        OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) THEN (pg_options_to_table(um.umoptions)).option_value
                       ELSE NULL END AS character_data) AS option_value
      FROM _pg_user_mappings um;
  
--- 2884,2895 ----
             CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
             CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
                         OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
!                        OR (
!                             SELECT has_role_attribute(pg_authid.oid, 'SUPERUSER') AS rolsuper
!                             FROM pg_authid
!                             WHERE rolname = current_user
!                           )
!                        THEN (pg_options_to_table(um.umoptions)).option_value
                       ELSE NULL END AS character_data) AS option_value
      FROM _pg_user_mappings um;
  
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index a819952..8e02116
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 9,25 ****
  CREATE VIEW pg_roles AS
      SELECT
          rolname,
!         rolsuper,
!         rolinherit,
!         rolcreaterole,
!         rolcreatedb,
!         rolcatupdate,
!         rolcanlogin,
!         rolreplication,
          rolconnlimit,
          '********'::text as rolpassword,
          rolvaliduntil,
-         rolbypassrls,
          setconfig as rolconfig,
          pg_authid.oid
      FROM pg_authid LEFT JOIN pg_db_role_setting s
--- 9,25 ----
  CREATE VIEW pg_roles AS
      SELECT
          rolname,
!         has_role_attribute(pg_authid.oid, 'SUPERUSER') AS rolsuper,
!         has_role_attribute(pg_authid.oid, 'INHERIT') AS rolinherit,
!         has_role_attribute(pg_authid.oid, 'CREATEROLE') AS rolcreaterole,
!         has_role_attribute(pg_authid.oid, 'CREATEDB') AS rolcreatedb,
!         has_role_attribute(pg_authid.oid, 'CATUPDATE') AS rolcatupdate,
!         has_role_attribute(pg_authid.oid, 'CANLOGIN') AS rolcanlogin,
!         has_role_attribute(pg_authid.oid, 'REPLICATION') AS rolreplication,
!         has_role_attribute(pg_authid.oid, 'BYPASSRLS') AS rolbypassrls,
          rolconnlimit,
          '********'::text as rolpassword,
          rolvaliduntil,
          setconfig as rolconfig,
          pg_authid.oid
      FROM pg_authid LEFT JOIN pg_db_role_setting s
*************** CREATE VIEW pg_shadow AS
*** 29,44 ****
      SELECT
          rolname AS usename,
          pg_authid.oid AS usesysid,
!         rolcreatedb AS usecreatedb,
!         rolsuper AS usesuper,
!         rolcatupdate AS usecatupd,
!         rolreplication AS userepl,
          rolpassword AS passwd,
          rolvaliduntil::abstime AS valuntil,
          setconfig AS useconfig
      FROM pg_authid LEFT JOIN pg_db_role_setting s
      ON (pg_authid.oid = setrole AND setdatabase = 0)
!     WHERE rolcanlogin;
  
  REVOKE ALL on pg_shadow FROM public;
  
--- 29,44 ----
      SELECT
          rolname AS usename,
          pg_authid.oid AS usesysid,
!         has_role_attribute(pg_authid.oid, 'CREATEDB') AS usecreatedb,
!         has_role_attribute(pg_authid.oid, 'SUPERUSER') AS usesuper,
!         has_role_attribute(pg_authid.oid, 'CATUPDATE') AS usecatupd,
!         has_role_attribute(pg_authid.oid, 'REPLICATION') AS userepl,
          rolpassword AS passwd,
          rolvaliduntil::abstime AS valuntil,
          setconfig AS useconfig
      FROM pg_authid LEFT JOIN pg_db_role_setting s
      ON (pg_authid.oid = setrole AND setdatabase = 0)
!     WHERE has_role_attribute(pg_authid.oid, 'CANLOGIN');
  
  REVOKE ALL on pg_shadow FROM public;
  
*************** CREATE VIEW pg_group AS
*** 48,54 ****
          oid AS grosysid,
          ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
      FROM pg_authid
!     WHERE NOT rolcanlogin;
  
  CREATE VIEW pg_user AS
      SELECT
--- 48,54 ----
          oid AS grosysid,
          ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
      FROM pg_authid
!     WHERE NOT has_role_attribute(pg_authid.oid, 'CANLOGIN');
  
  CREATE VIEW pg_user AS
      SELECT
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
new file mode 100644
index 94c82d3..78dae2d
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
*************** get_db_info(const char *name, LOCKMODE l
*** 1812,1831 ****
  static bool
  have_createdb_privilege(void)
  {
- 	bool		result = false;
- 	HeapTuple	utup;
- 
  	/* Superusers can always do everything */
  	if (superuser())
  		return true;
  
! 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
! 	if (HeapTupleIsValid(utup))
! 	{
! 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb;
! 		ReleaseSysCache(utup);
! 	}
! 	return result;
  }
  
  /*
--- 1812,1822 ----
  static bool
  have_createdb_privilege(void)
  {
  	/* Superusers can always do everything */
  	if (superuser())
  		return true;
  
! 	return role_has_attribute(GetUserId(), ROLE_ATTR_CREATEDB);
  }
  
  /*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 1a73fd8..72c5dcc
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** have_createrole_privilege(void)
*** 63,68 ****
--- 63,73 ----
  	return has_createrole_privilege(GetUserId());
  }
  
+ static RoleAttr
+ set_role_attribute(RoleAttr attributes, RoleAttr attribute)
+ {
+ 	return ((attributes & ~(0xFFFFFFFFFFFFFFFF)) | attribute);
+ }
  
  /*
   * CREATE ROLE
*************** CreateRole(CreateRoleStmt *stmt)
*** 81,93 ****
  	char	   *password = NULL;	/* user password */
  	bool		encrypt_password = Password_encryption; /* encrypt password? */
  	char		encrypted_password[MD5_PASSWD_LEN + 1];
! 	bool		issuper = false;	/* Make the user a superuser? */
! 	bool		inherit = true; /* Auto inherit privileges? */
  	bool		createrole = false;		/* Can this user create roles? */
  	bool		createdb = false;		/* Can the user create databases? */
  	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? */
  	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 */
--- 86,99 ----
  	char	   *password = NULL;	/* user password */
  	bool		encrypt_password = Password_encryption; /* encrypt password? */
  	char		encrypted_password[MD5_PASSWD_LEN + 1];
! 	bool		issuper = false;		/* Make the user a superuser? */
! 	bool		inherit = true;			/* Auto inherit privileges? */
  	bool		createrole = false;		/* Can this user create roles? */
  	bool		createdb = false;		/* Can the user create databases? */
  	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? */
+ 	RoleAttr	attributes = ROLE_ATTR_NONE;	/* role attributes, initialized to none. */
  	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)
*** 249,254 ****
--- 255,262 ----
  
  	if (dpassword && dpassword->arg)
  		password = strVal(dpassword->arg);
+ 
+ 	/* Role Attributes */
  	if (dissuper)
  		issuper = intVal(dissuper->arg) != 0;
  	if (dinherit)
*************** CreateRole(CreateRoleStmt *stmt)
*** 261,266 ****
--- 269,277 ----
  		canlogin = intVal(dcanlogin->arg) != 0;
  	if (disreplication)
  		isreplication = intVal(disreplication->arg) != 0;
+ 	if (dbypassRLS)
+ 		bypassrls = intVal(dbypassRLS->arg) != 0;
+ 
  	if (dconnlimit)
  	{
  		connlimit = intVal(dconnlimit->arg);
*************** CreateRole(CreateRoleStmt *stmt)
*** 277,284 ****
  		adminmembers = (List *) dadminmembers->arg;
  	if (dvalidUntil)
  		validUntil = strVal(dvalidUntil->arg);
- 	if (dbypassRLS)
- 		bypassrls = intVal(dbypassRLS->arg) != 0;
  
  	/* Check some permissions first */
  	if (issuper)
--- 288,293 ----
*************** CreateRole(CreateRoleStmt *stmt)
*** 355,360 ****
--- 364,385 ----
  								validUntil_datum,
  								validUntil_null);
  
+ 	/* Set all role attributes */
+ 	if (issuper)
+ 		attributes |= ROLE_ATTR_SUPERUSER;
+ 	if (inherit)
+ 		attributes |= ROLE_ATTR_INHERIT;
+ 	if (createrole)
+ 		attributes |= ROLE_ATTR_CREATEROLE;
+ 	if (createdb)
+ 		attributes |= ROLE_ATTR_CREATEDB;
+ 	if (canlogin)
+ 		attributes |= ROLE_ATTR_CANLOGIN;
+ 	if (isreplication)
+ 		attributes |= ROLE_ATTR_REPLICATION;
+ 	if (bypassrls)
+ 		attributes |= ROLE_ATTR_BYPASSRLS;
+ 
  	/*
  	 * Build a tuple to insert
  	 */
*************** CreateRole(CreateRoleStmt *stmt)
*** 364,377 ****
  	new_record[Anum_pg_authid_rolname - 1] =
  		DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
  
! 	new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
! 	new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
! 	new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
! 	new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
! 	/* superuser gets catupdate right by default */
! 	new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
! 	new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
! 	new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
  	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
  
  	if (password)
--- 389,396 ----
  	new_record[Anum_pg_authid_rolname - 1] =
  		DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
  
! 	new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
! 
  	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
  
  	if (password)
*************** CreateRole(CreateRoleStmt *stmt)
*** 394,401 ****
  	new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  
- 	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
- 
  	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
  
  	/*
--- 413,418 ----
*************** AlterRole(AlterRoleStmt *stmt)
*** 508,513 ****
--- 525,531 ----
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
  	Oid			roleid;
+ 	RoleAttr attributes;
  
  	/* Extract options from the statement node tree */
  	foreach(option, stmt->options)
*************** AlterRole(AlterRoleStmt *stmt)
*** 661,681 ****
  	 * To mess with a superuser you gotta be superuser; else you need
  	 * createrole, or just want to change your own password
  	 */
! 	if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter superusers")));
  	}
! 	else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter replication users")));
  	}
! 	else if (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
--- 679,702 ----
  	 * To mess with a superuser you gotta be superuser; else you need
  	 * createrole, or just want to change your own password
  	 */
! 
! 	attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
! 
! 	if (((attributes & ROLE_ATTR_SUPERUSER) > 0) || issuper >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter superusers")));
  	}
! 	else if (((attributes & ROLE_ATTR_REPLICATION) > 0) || isreplication >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter replication users")));
  	}
! 	else if (((attributes & ROLE_ATTR_BYPASSRLS) > 0) || bypassrls >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
*************** AlterRole(AlterRoleStmt *stmt)
*** 743,785 ****
  	 */
  	if (issuper >= 0)
  	{
! 		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
! 		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
! 
! 		new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
! 		new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
  	}
  
  	if (inherit >= 0)
  	{
! 		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
! 		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
  	}
  
  	if (createrole >= 0)
  	{
! 		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
! 		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
  	}
  
  	if (createdb >= 0)
  	{
! 		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
! 		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
  	}
  
  	if (canlogin >= 0)
  	{
! 		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
! 		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
  	}
  
  	if (isreplication >= 0)
  	{
! 		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
! 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
  	}
  
  	if (dconnlimit)
  	{
  		new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
--- 764,821 ----
  	 */
  	if (issuper >= 0)
  	{
! 		attributes = set_role_attribute(attributes,
! 							(issuper > 0) ? (ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE) :
! 							~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE));
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (inherit >= 0)
  	{
! 		attributes = set_role_attribute(attributes,
! 							(inherit > 0) ? ROLE_ATTR_INHERIT : ~(ROLE_ATTR_INHERIT));
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (createrole >= 0)
  	{
! 		attributes = set_role_attribute(attributes,
! 							(createrole > 0) ? ROLE_ATTR_CREATEROLE : ~(ROLE_ATTR_CREATEROLE));
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (createdb >= 0)
  	{
! 		attributes = set_role_attribute(attributes,
! 							(createdb > 0) ? ROLE_ATTR_CREATEDB : ~(ROLE_ATTR_CREATEDB));
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (canlogin >= 0)
  	{
! 		attributes = set_role_attribute(attributes,
! 							(canlogin > 0) ? ROLE_ATTR_CANLOGIN : ~(ROLE_ATTR_CANLOGIN));
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (isreplication >= 0)
  	{
! 		attributes = set_role_attribute(attributes,
! 							(isreplication > 0) ? ROLE_ATTR_REPLICATION : ~(ROLE_ATTR_REPLICATION));
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
+ 	if (bypassrls >= 0)
+ 	{
+ 		attributes = set_role_attribute(attributes,
+ 							(bypassrls > 0) ? ROLE_ATTR_BYPASSRLS : ~(ROLE_ATTR_BYPASSRLS));
+ 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
+ 	}
+ 
+ 	/* If any role attributes were set, then update. */
+ 	if (new_record_repl[Anum_pg_authid_rolattr - 1])
+ 		new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
+ 
  	if (dconnlimit)
  	{
  		new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
*************** AlterRole(AlterRoleStmt *stmt)
*** 815,825 ****
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  	new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
  
- 	if (bypassrls >= 0)
- 	{
- 		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
- 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
- 	}
  
  	new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
  								  new_record_nulls, new_record_repl);
--- 851,856 ----
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 867,872 ****
--- 898,904 ----
  	HeapTuple	roletuple;
  	Oid			databaseid = InvalidOid;
  	Oid			roleid = InvalidOid;
+ 	RoleAttr	attributes;
  
  	if (stmt->role)
  	{
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 889,895 ****
  		 * To mess with a superuser you gotta be superuser; else you need
  		 * createrole, or just want to change your own settings
  		 */
! 		if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
  		{
  			if (!superuser())
  				ereport(ERROR,
--- 921,928 ----
  		 * To mess with a superuser you gotta be superuser; else you need
  		 * createrole, or just want to change your own settings
  		 */
! 		attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr;
! 		if ((attributes & ROLE_ATTR_SUPERUSER) > 0)
  		{
  			if (!superuser())
  				ereport(ERROR,
*************** DropRole(DropRoleStmt *stmt)
*** 973,978 ****
--- 1006,1012 ----
  		char	   *detail_log;
  		SysScanDesc sscan;
  		Oid			roleid;
+ 		RoleAttr	attributes;
  
  		tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
  		if (!HeapTupleIsValid(tuple))
*************** DropRole(DropRoleStmt *stmt)
*** 1013,1020 ****
  		 * roles but not superuser roles.  This is mainly to avoid the
  		 * scenario where you accidentally drop the last superuser.
  		 */
! 		if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
! 			!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to drop superusers")));
--- 1047,1054 ----
  		 * roles but not superuser roles.  This is mainly to avoid the
  		 * scenario where you accidentally drop the last superuser.
  		 */
! 		attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
! 		if (((attributes & ROLE_ATTR_SUPERUSER) > 0) && !superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to drop superusers")));
*************** RenameRole(const char *oldname, const ch
*** 1128,1133 ****
--- 1162,1168 ----
  	bool		repl_repl[Natts_pg_authid];
  	int			i;
  	Oid			roleid;
+ 	RoleAttr	attributes;
  
  	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
  	dsc = RelationGetDescr(rel);
*************** RenameRole(const char *oldname, const ch
*** 1173,1179 ****
  	/*
  	 * createrole is enough privilege unless you want to mess with a superuser
  	 */
! 	if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
  	{
  		if (!superuser())
  			ereport(ERROR,
--- 1208,1215 ----
  	/*
  	 * createrole is enough privilege unless you want to mess with a superuser
  	 */
! 	attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
! 	if ((attributes & ROLE_ATTR_SUPERUSER) > 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
new file mode 100644
index 6ce8dae..a8a9f2e
*** a/src/backend/commands/variable.c
--- b/src/backend/commands/variable.c
*************** check_session_authorization(char **newva
*** 776,781 ****
--- 776,782 ----
  	Oid			roleid;
  	bool		is_superuser;
  	role_auth_extra *myextra;
+ 	RoleAttr	attributes;
  
  	/* Do nothing for the boot_val default of NULL */
  	if (*newval == NULL)
*************** check_session_authorization(char **newva
*** 800,806 ****
  	}
  
  	roleid = HeapTupleGetOid(roleTup);
! 	is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
  
  	ReleaseSysCache(roleTup);
  
--- 801,808 ----
  	}
  
  	roleid = HeapTupleGetOid(roleTup);
! 	attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
! 	is_superuser = ((attributes & ROLE_ATTR_SUPERUSER) > 0);
  
  	ReleaseSysCache(roleTup);
  
*************** check_role(char **newval, void **extra,
*** 844,849 ****
--- 846,852 ----
  	Oid			roleid;
  	bool		is_superuser;
  	role_auth_extra *myextra;
+ 	RoleAttr	attributes;
  
  	if (strcmp(*newval, "none") == 0)
  	{
*************** check_role(char **newval, void **extra,
*** 872,878 ****
  		}
  
  		roleid = HeapTupleGetOid(roleTup);
! 		is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
  
  		ReleaseSysCache(roleTup);
  
--- 875,882 ----
  		}
  
  		roleid = HeapTupleGetOid(roleTup);
! 		attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
! 		is_superuser = ((attributes & ROLE_ATTR_SUPERUSER) > 0);
  
  		ReleaseSysCache(roleTup);
  
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
new file mode 100644
index dc6eb2c..f3108a5
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
*************** static Oid	convert_type_name(text *typen
*** 115,120 ****
--- 115,121 ----
  static AclMode convert_type_priv_string(text *priv_type_text);
  static AclMode convert_role_priv_string(text *priv_type_text);
  static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
+ static RoleAttr convert_role_attr_string(text *attr_type_text);
  
  static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
  
*************** aclitemin(PG_FUNCTION_ARGS)
*** 577,582 ****
--- 578,584 ----
  	PG_RETURN_ACLITEM_P(aip);
  }
  
+ 
  /*
   * aclitemout
   *		Allocates storage for, and fills in, a new null-delimited string
*************** pg_role_aclcheck(Oid role_oid, Oid rolei
*** 4602,4607 ****
--- 4604,4655 ----
  	return ACLCHECK_NO_PRIV;
  }
  
+ /*
+  * has_role_attribute_id
+  *		Check the named role attribute on a role by given role oid.
+  */
+ Datum
+ has_role_attribute_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			roleoid = PG_GETARG_OID(0);
+ 	text	   *attr_type_text = PG_GETARG_TEXT_P(1);
+ 	RoleAttr	attribute;
+ 
+ 	attribute = convert_role_attr_string(attr_type_text);
+ 
+ 	PG_RETURN_BOOL(role_has_attribute(roleoid, attribute));
+ }
+ 
+ /*
+  * convert_role_attr_string
+  *		Convert text string to RoleAttr value.
+  */
+ static RoleAttr
+ convert_role_attr_string(text *attr_type_text)
+ {
+ 	char	   *attr_type = text_to_cstring(attr_type_text);
+ 
+ 	if (pg_strcasecmp(attr_type, "SUPERUSER") == 0)
+ 		return ROLE_ATTR_SUPERUSER;
+ 	else if (pg_strcasecmp(attr_type, "INHERIT") == 0)
+ 		return ROLE_ATTR_INHERIT;
+ 	else if (pg_strcasecmp(attr_type, "CREATEROLE") == 0)
+ 		return ROLE_ATTR_CREATEROLE;
+ 	else if (pg_strcasecmp(attr_type, "CREATEDB") == 0)
+ 		return ROLE_ATTR_CREATEDB;
+ 	else if (pg_strcasecmp(attr_type, "CATUPDATE") == 0)
+ 		return ROLE_ATTR_CATUPDATE;
+ 	else if (pg_strcasecmp(attr_type, "CANLOGIN") == 0)
+ 		return ROLE_ATTR_CANLOGIN;
+ 	else if (pg_strcasecmp(attr_type, "REPLICATION") == 0)
+ 		return ROLE_ATTR_REPLICATION;
+ 	else if (pg_strcasecmp(attr_type, "BYPASSRLS") == 0)
+ 		return ROLE_ATTR_BYPASSRLS;
+ 	else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("unrecognized role attribute: \"%s\"", attr_type)));
+ }
  
  /*
   * initialization function (called by InitPostgres)
*************** RoleMembershipCacheCallback(Datum arg, i
*** 4638,4653 ****
  static bool
  has_rolinherit(Oid roleid)
  {
! 	bool		result = false;
  	HeapTuple	utup;
  
  	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(utup))
  	{
! 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
  		ReleaseSysCache(utup);
  	}
! 	return result;
  }
  
  
--- 4686,4701 ----
  static bool
  has_rolinherit(Oid roleid)
  {
! 	RoleAttr	attributes;
  	HeapTuple	utup;
  
  	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(utup))
  	{
! 		attributes = ((Form_pg_authid) GETSTRUCT(utup))->rolattr;
  		ReleaseSysCache(utup);
  	}
! 	return ((attributes & ROLE_ATTR_INHERIT) > 0);
  }
  
  
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
new file mode 100644
index 8fccb4c..51bf035
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
*************** SetUserIdAndContext(Oid userid, bool sec
*** 334,349 ****
  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;
  }
  
  /*
--- 334,349 ----
  bool
  has_rolreplication(Oid roleid)
  {
! 	RoleAttr	attributes;
  	HeapTuple	utup;
  
  	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(utup))
  	{
! 		attributes = ((Form_pg_authid) GETSTRUCT(utup))->rolattr;
  		ReleaseSysCache(utup);
  	}
! 	return ((attributes & ROLE_ATTR_REPLICATION) > 0);
  }
  
  /*
*************** InitializeSessionUserId(const char *role
*** 375,381 ****
  	roleid = HeapTupleGetOid(roleTup);
  
  	AuthenticatedUserId = roleid;
! 	AuthenticatedUserIsSuperuser = rform->rolsuper;
  
  	/* This sets OuterUserId/CurrentUserId too */
  	SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
--- 375,381 ----
  	roleid = HeapTupleGetOid(roleTup);
  
  	AuthenticatedUserId = roleid;
! 	AuthenticatedUserIsSuperuser = ((rform->rolattr & ROLE_ATTR_SUPERUSER) > 0);
  
  	/* This sets OuterUserId/CurrentUserId too */
  	SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
*************** InitializeSessionUserId(const char *role
*** 394,400 ****
  		/*
  		 * Is role allowed to login at all?
  		 */
! 		if (!rform->rolcanlogin)
  			ereport(FATAL,
  					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
  					 errmsg("role \"%s\" is not permitted to log in",
--- 394,400 ----
  		/*
  		 * Is role allowed to login at all?
  		 */
! 		if (!((rform->rolattr & ROLE_ATTR_CANLOGIN) > 0))
  			ereport(FATAL,
  					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
  					 errmsg("role \"%s\" is not permitted to log in",
diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c
new file mode 100644
index ff0f947..c1ea1c4
*** a/src/backend/utils/misc/superuser.c
--- b/src/backend/utils/misc/superuser.c
*************** superuser_arg(Oid roleid)
*** 58,63 ****
--- 58,64 ----
  {
  	bool		result;
  	HeapTuple	rtup;
+ 	RoleAttr	attributes;
  
  	/* Quick out for cache hit */
  	if (OidIsValid(last_roleid) && last_roleid == roleid)
*************** superuser_arg(Oid roleid)
*** 71,77 ****
  	rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(rtup))
  	{
! 		result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper;
  		ReleaseSysCache(rtup);
  	}
  	else
--- 72,79 ----
  	rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(rtup))
  	{
! 		attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr;
! 		result = ((attributes & ROLE_ATTR_SUPERUSER) > 0);
  		ReleaseSysCache(rtup);
  	}
  	else
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
new file mode 100644
index 3b63d2b..54c3098
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
***************
*** 22,27 ****
--- 22,28 ----
  #define PG_AUTHID_H
  
  #include "catalog/genbki.h"
+ #include "nodes/parsenodes.h"
  
  /*
   * The CATALOG definition has to refer to the type of rolvaliduntil as
***************
*** 45,60 ****
  CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
  {
  	NameData	rolname;		/* name of role */
! 	bool		rolsuper;		/* read this field via superuser() only! */
! 	bool		rolinherit;		/* inherit privileges from other roles? */
! 	bool		rolcreaterole;	/* allowed to create more roles? */
! 	bool		rolcreatedb;	/* allowed to create databases? */
! 	bool		rolcatupdate;	/* allowed to alter catalogs manually? */
! 	bool		rolcanlogin;	/* allowed to log in as session user? */
! 	bool		rolreplication; /* role used for streaming replication */
! 	bool		rolbypassrls;	/* allowed to bypass row level security? */
  	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
- 
  	/* remaining fields may be null; use heap_getattr to read them! */
  	text		rolpassword;	/* password, if any */
  	timestamptz rolvaliduntil;	/* password expiration time, if any */
--- 46,53 ----
  CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
  {
  	NameData	rolname;		/* name of role */
! 	int64		rolattr;		/* role attribute bitmask */
  	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
  	/* remaining fields may be null; use heap_getattr to read them! */
  	text		rolpassword;	/* password, if any */
  	timestamptz rolvaliduntil;	/* password expiration time, if any */
*************** typedef FormData_pg_authid *Form_pg_auth
*** 74,92 ****
   *		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
! #define Anum_pg_authid_rolcreaterole	4
! #define Anum_pg_authid_rolcreatedb		5
! #define Anum_pg_authid_rolcatupdate		6
! #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
--- 67,78 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					5
  #define Anum_pg_authid_rolname			1
! #define Anum_pg_authid_rolattr			2
! #define Anum_pg_authid_rolconnlimit		3
! #define Anum_pg_authid_rolpassword		4
! #define Anum_pg_authid_rolvaliduntil	5
  
  /* ----------------
   *		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
  
--- 81,87 ----
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" 255 -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 5d4e889..c818e51
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("current user privilege on any col
*** 2676,2681 ****
--- 2676,2684 ----
  DATA(insert OID = 3029 (  has_any_column_privilege	   PGNSP PGUID 12 10 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_any_column_privilege_id _null_ _null_ _null_ ));
  DESCR("current user privilege on any column by rel oid");
  
+ DATA(insert OID = 6000 (  has_role_attribute		   PGNSP PGUID 12 10 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_role_attribute_id _null_ _null_ _null_));
+ DESCR("user role attribute by user oid");
+ 
  DATA(insert OID = 1928 (  pg_stat_get_numscans			PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_numscans _null_ _null_ _null_ ));
  DESCR("statistics: number of scans done for table/index");
  DATA(insert OID = 1929 (  pg_stat_get_tuples_returned	PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_tuples_returned _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 3e4f815..d560d69
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef uint32 AclMode;			/* a bitmask o
*** 78,83 ****
--- 78,101 ----
  /* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
  #define ACL_SELECT_FOR_UPDATE	ACL_UPDATE
  
+ /*
+  * Role attributes are encoded so that we can OR them together in a bitmask.
+  * The present representation of RoleAttr limits us to 64 distinct rights.
+  *
+  * Caution: changing these codes breaks stored RoleAttrs, hence forces initdb.
+  */
+ typedef uint64 RoleAttr;		/* a bitmask for role attribute bits */
+ 
+ #define ROLE_ATTR_SUPERUSER		(1<<0)
+ #define ROLE_ATTR_INHERIT		(1<<1)
+ #define ROLE_ATTR_CREATEROLE	(1<<2)
+ #define ROLE_ATTR_CREATEDB		(1<<3)
+ #define ROLE_ATTR_CATUPDATE		(1<<4)
+ #define ROLE_ATTR_CANLOGIN		(1<<5)
+ #define ROLE_ATTR_REPLICATION	(1<<6)
+ #define ROLE_ATTR_BYPASSRLS		(1<<7)
+ #define N_ROLE_ATTRIBUTES		8
+ #define ROLE_ATTR_NONE			0
  
  /*****************************************************************************
   *	Query Tree
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
new file mode 100644
index a8e3164..aecad4f
*** 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 role_has_attribute(Oid roleid, RoleAttr capability);
  
  #endif   /* ACL_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index 417fd17..31d7559
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_has_role_id_name(PG_FUNC
*** 106,111 ****
--- 106,112 ----
  extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS);
  extern Datum pg_has_role_name(PG_FUNCTION_ARGS);
  extern Datum pg_has_role_id(PG_FUNCTION_ARGS);
+ extern Datum has_role_attribute_id(PG_FUNCTION_ARGS);
  
  /* bool.c */
  extern Datum boolin(PG_FUNCTION_ARGS);
#41José Luis Tallón
jltallon@adv-solutions.net
In reply to: Robert Haas (#35)
Re: Additional role attributes && superuser review

On 11/06/2014 03:31 AM, Robert Haas wrote:

[snip]

We haven't reached consensus on this one yet and I didn't want it to fall
too far off the radar.

Here is what I summarize as the current state of the discussion:

1. Syntax:

ALTER ROLE <role> { ADD | DROP } CAPABILITY <capability>

Though a bit late to this thread, I would like to request comments on
potentially beneficial new roles ?? and/or capabilities which I have
recently found needing myself.
The suggested syntax looks intuitive and potentially very flexible.
I'll try to summarize up what I recall from the thread plus my own
itchs, to try and get others to comment and expand on the matter.

We currently have:
* SUPERUSER / CREATEUSER
* CREATEDB
* CREATEROLE
* LOGIN
* REPLICATION

(plus INHERITS and ADMIN options, of course)

It has also been suggested to include a
* BACKUP role (capability?) i.e. ability to take an snapshot and
read all relations, views, triggers and functions (even bypassing RLS)
and the catalog in order to produce a full, consistent dump of the whole
cluster.

and I seem to recall something along the lines of
* AUDIT, potentially limited to just engage

I am hereby suggesting the addition of a
* MAINTENANCE role, which would be able to perform VACUUM, ANALYZE,
REINDEX *CONCURRENTLY* and REFRESH MATERIALIZED VIEW *CONCURRENTLY* ...
and potentially even ALTER TABLE VALIDATE CONSTRAINT (if we are able to
produce a non-blocking/fully concurrent version)

... which might become very useful for DBAs wishing to use some
password-less roles for scheduled maintenance routines while at the same
time reducing the exposure.

While at it, the replication role might as well gain the ability to
promote/demote a cluster (standby<->active), or shall it be some kind of
FAILOVER role/capability ?

Thanks in advance.

/ J.L.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Stephen Frost
sfrost@snowman.net
In reply to: Andrew Dunstan (#39)
Re: Additional role attributes && superuser review

* Andrew Dunstan (andrew@dunslane.net) wrote:

On 11/18/2014 04:58 PM, Adam Brightwell wrote:

Attached is a quite trivial patch that addresses the int64 (C) to
int8 (SQL) mapping issue.

I think we should just apply this now. As Tom said the reason for
not doing it is long gone.

Alright, done.

Thanks!

Stephen

#43Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Stephen Frost (#42)
Re: Additional role attributes && superuser review

All,

I want to revive this thread and continue to move these new role attributes
forward.

In summary, the ultimate goal is to include new role attributes for common
operations which currently require superuser privileges.

Initially proposed were the following attributes:

* BACKUP - allows role to perform backup operations
* LOGROTATE - allows role to rotate log files
* MONITOR - allows role to view pg_stat_* details
* PROCSIGNAL - allows role to signal backend processes

It seems that PROCSIGNAL and MONITOR were generally well received and
probably don't warrant much more discussion at this point.

However, based on previous discussions, there seemed to be some uncertainty
on how to handle BACKUP and LOGROTATE.

Concerns:

* LOGROTATE - only associated with one function/operation.
* BACKUP - perceived to be too broad of a permission as it it would provide
the ability to run pg_start/stop_backend and the xlog related functions.
It is general sentiment is that these should be handled as separate
privileges.
* BACKUP - preferred usage is with pg_dump to giving a user the ability to
run pg_dump on the whole database without being superuser.

Previous Recommendations:

* LOGROTATE - Use OPERATOR - concern was expressed that this might be too
general of an attribute for this purpose. Also, concern for privilege
'upgrades' as it includes more capabilities in later releases.
* LOGROTATE - Use LOG_OPERATOR - generally accepted, but concern was raise
for using extraneous descriptors such as '_OPERATOR' and '_ADMIN', etc.
* BACKUP - Use WAL_CONTROL for pg_start/stop_backup - no major
disagreement, though same concern regarding extraneous descriptors.
* BACKUP - Use XLOG_OPERATOR for xlog operations - no major disagreement,
though same concern regarding extraneous descriptors.
* BACKUP - Use BACKUP for granting non-superuser ability to run pg_dump on
whole database.

Given the above and previous discussions:

I'd like to propose the following new role attributes:

BACKUP - allows role to perform pg_dump* backups of whole database.
WAL - allows role to execute pg_start_backup/pg_stop_backup functions.
XLOG - allows role to execute xlog operations.
LOG - allows role to rotate log files - remains broad enough to consider
future log related operations.
MONITOR - allows role to view pg_stat_* details.
PROCSIGNAL - allows role to signal backend processes.

If these seem reasonable, then I'll begin updating the initial/current
patch submitted. But in either case, feedback and suggestions are
certainly welcome and appreciated.

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#44Magnus Hagander
magnus@hagander.net
In reply to: Adam Brightwell (#43)
Re: Additional role attributes && superuser review

On Wed, Dec 24, 2014 at 6:48 PM, Adam Brightwell <
adam.brightwell@crunchydatasolutions.com> wrote:

All,

I want to revive this thread and continue to move these new role
attributes forward.

In summary, the ultimate goal is to include new role attributes for common
operations which currently require superuser privileges.

Initially proposed were the following attributes:

* BACKUP - allows role to perform backup operations
* LOGROTATE - allows role to rotate log files
* MONITOR - allows role to view pg_stat_* details
* PROCSIGNAL - allows role to signal backend processes

It seems that PROCSIGNAL and MONITOR were generally well received and
probably don't warrant much more discussion at this point.

However, based on previous discussions, there seemed to be some
uncertainty on how to handle BACKUP and LOGROTATE.

Concerns:

* LOGROTATE - only associated with one function/operation.
* BACKUP - perceived to be too broad of a permission as it it would
provide the ability to run pg_start/stop_backend and the xlog related
functions. It is general sentiment is that these should be handled as
separate privileges.

* BACKUP - preferred usage is with pg_dump to giving a user the ability to

run pg_dump on the whole database without being superuser.

Previous Recommendations:

* LOGROTATE - Use OPERATOR - concern was expressed that this might be too
general of an attribute for this purpose. Also, concern for privilege
'upgrades' as it includes more capabilities in later releases.
* LOGROTATE - Use LOG_OPERATOR - generally accepted, but concern was raise
for using extraneous descriptors such as '_OPERATOR' and '_ADMIN', etc.
* BACKUP - Use WAL_CONTROL for pg_start/stop_backup - no major
disagreement, though same concern regarding extraneous descriptors.
* BACKUP - Use XLOG_OPERATOR for xlog operations - no major disagreement,
though same concern regarding extraneous descriptors.
* BACKUP - Use BACKUP for granting non-superuser ability to run pg_dump on
whole database.

Given the above and previous discussions:

I'd like to propose the following new role attributes:

BACKUP - allows role to perform pg_dump* backups of whole database.

I'd suggest it's called DUMP if that's what it allows, to keep it separate
from the backup parts.

WAL - allows role to execute pg_start_backup/pg_stop_backup functions.

XLOG - allows role to execute xlog operations.

That seems really bad names, IMHO. Why? Because we use WAL and XLOG
throughout documentation and parameters and code to mean *the same thing*.
And here they'd suddenly mean different things. If we need them as separate
privileges, I think we need much better names. (And a better description -
what is "xlog operations" really?)

And the one under WAL would be very similar to what REPLICATION does today.
Or are you saying that it should specifically *not* allow base backups done
through the replication protocol, only the exclusive ones?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#45Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Magnus Hagander (#44)
Re: Additional role attributes && superuser review

Magnus,

Thanks for the feedback.

BACKUP - allows role to perform pg_dump* backups of whole database.

I'd suggest it's called DUMP if that's what it allows, to keep it separate
from the backup parts.

Makes sense to me.

That seems really bad names, IMHO. Why? Because we use WAL and XLOG

throughout documentation and parameters and code to mean *the same thing*.
And here they'd suddenly mean different things. If we need them as separate
privileges, I think we need much better names. (And a better description -
what is "xlog operations" really?)

Fair enough, ultimately what I was trying to address is the following
concern raised by Alvaro:

"To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say."

Upon re-reading it (and other discussions around it) I believe that I must
have misinterpreted. Initially, I read it to mean that we needed the
following separate permissions.

1) ability to pg_dump
2) ability to start/stop backups
3) ability to execute xlog related functions

When indeed, what it meant was to have the following separate (effectively
merging #2 and #3):

1) ability to pg_dump
2) ability to start/stop backups *and* ability to execute xlog related
functions.

My apologies on that confusion.

Given this clarification:

I think #1 could certainly be answered by using DUMP. I have no strong
opinion in either direction, though I do think that BACKUP does make the
most sense for #2. Previously, Stephen had mentioned a READONLY capability
that could effectively work for pg_dump, though, Jim's suggestion of
keeping 'read-all' separate from 'ability to pg_dump' seems logical. In
either case, I certainly wouldn't mind having a wider agreement/consensus
on this approach.

So, here is a revised list of proposed attributes:

* BACKUP - allows role to perform backup operations (as originally proposed)
* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations
* DUMP - allows role to perform pg_dump* backups of whole database
* MONITOR - allows role to view pg_stat_* details (as originally proposed)
* PROCSIGNAL - allows role to signal backend processes (as originally
proposed)well

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#46Stephen Frost
sfrost@snowman.net
In reply to: Adam Brightwell (#45)
Re: Additional role attributes && superuser review

Adam,

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

I'd suggest it's called DUMP if that's what it allows, to keep it separate
from the backup parts.

Makes sense to me.

I'm fine calling it 'DUMP', but for different reasons.

We have no (verifiable) idea what client program is being used to
connect and therefore we shouldn't try to tie the name of the client
program to the permission.

That said, a 'DUMP' privilege which allows the user to dump the contents
of the entire database is entirely reasonable. We need to be clear in
the documentation though- such a 'DUMP' privilege is essentially
granting USAGE on all schemas and table-level SELECT on all tables and
sequences (anything else..?). To be clear, a user with this privilege
can utilize that access without using pg_dump.

One other point is that this shouldn't imply any other privileges, imv.
I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things
should work 'sanely' with any combination of the two options.
Similairly, DUMP shouldn't imply BACKUP or visa-versa. In particular,
I'd like to see roles which have only the BACKUP privilege be unable to
directly read any data (modulo things granted to PUBLIC). This would
allow an unprivileged user to manage backups, kick off ad-hoc ones, etc,
without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but that's
entirely possible with more advanced SANs and enterprise backup
solutions).

That seems really bad names, IMHO. Why? Because we use WAL and XLOG
throughout documentation and parameters and code to mean *the same thing*.
And here they'd suddenly mean different things. If we need them as separate
privileges, I think we need much better names. (And a better description -
what is "xlog operations" really?)

Fair enough, ultimately what I was trying to address is the following
concern raised by Alvaro:

"To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say."

Note that the BACKUP role attribute was never intended to cover the
pg_dump use-case. Simply the name of it caused confusion though. I'm
not sure if adding a DUMP role attribute is sufficient enough to address
that confusion, but I haven't got a better idea either.

When indeed, what it meant was to have the following separate (effectively
merging #2 and #3):

1) ability to pg_dump
2) ability to start/stop backups *and* ability to execute xlog related
functions.

That sounds reasonable to me (and is what was initially proposed, though
I've come around to the thinking that this BACKUP role attribute should
also allow pg_xlog_replay_pause/resume(), as those can be useful on
replicas).

Given this clarification:

I think #1 could certainly be answered by using DUMP. I have no strong
opinion in either direction, though I do think that BACKUP does make the
most sense for #2. Previously, Stephen had mentioned a READONLY capability
that could effectively work for pg_dump, though, Jim's suggestion of
keeping 'read-all' separate from 'ability to pg_dump' seems logical. In
either case, I certainly wouldn't mind having a wider agreement/consensus
on this approach.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

There is one issue that occurs to me, however. We're talking about
pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

So, here is a revised list of proposed attributes:

* BACKUP - allows role to perform backup operations (as originally proposed)
* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations
* DUMP - allows role to perform pg_dump* backups of whole database
* MONITOR - allows role to view pg_stat_* details (as originally proposed)
* PROCSIGNAL - allows role to signal backend processes (as originally
proposed)well

Looks reasonable to me.

Thanks!

Stephen

#47Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Stephen Frost (#46)
Re: Additional role attributes && superuser review

On 12/29/14, 4:01 PM, Stephen Frost wrote:

Adam,

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

I'd suggest it's called DUMP if that's what it allows, to keep it separate
from the backup parts.

Makes sense to me.

I'm fine calling it 'DUMP', but for different reasons.

We have no (verifiable) idea what client program is being used to
connect and therefore we shouldn't try to tie the name of the client
program to the permission.

That said, a 'DUMP' privilege which allows the user to dump the contents
of the entire database is entirely reasonable. We need to be clear in
the documentation though- such a 'DUMP' privilege is essentially
granting USAGE on all schemas and table-level SELECT on all tables and
sequences (anything else..?). To be clear, a user with this privilege
can utilize that access without using pg_dump.

Yeah... it may be better to call this something other than DUMP (see below).

One other point is that this shouldn't imply any other privileges, imv.
I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things
should work 'sanely' with any combination of the two options.

That does violate POLA, but it's probably worth doing so...

Similairly, DUMP shouldn't imply BACKUP or visa-versa. In particular,
I'd like to see roles which have only the BACKUP privilege be unable to
directly read any data (modulo things granted to PUBLIC). This would
allow an unprivileged user to manage backups, kick off ad-hoc ones, etc,
without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but that's
entirely possible with more advanced SANs and enterprise backup
solutions).

Yes, since a binary backup doesn't need to actually "read" data, it shouldn't implicitly allow a user granted that role to either.

Given this clarification:

I think #1 could certainly be answered by using DUMP. I have no strong
opinion in either direction, though I do think that BACKUP does make the
most sense for #2. Previously, Stephen had mentioned a READONLY capability
that could effectively work for pg_dump, though, Jim's suggestion of
keeping 'read-all' separate from 'ability to pg_dump' seems logical. In
either case, I certainly wouldn't mind having a wider agreement/consensus
on this approach.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

My specific concern about DUMP vs read all/only is IIRC dump needs to do more extensive locking than regular selects do, which can cause production problems.

There is one issue that occurs to me, however. We're talking about
pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Ugh, hadn't thought about that. :(

So, here is a revised list of proposed attributes:

* BACKUP - allows role to perform backup operations (as originally proposed)
* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations
* DUMP - allows role to perform pg_dump* backups of whole database
* MONITOR - allows role to view pg_stat_* details (as originally proposed)
* PROCSIGNAL - allows role to signal backend processes (as originally
proposed)well

Given the confusion that can exist with the data reading stuff, perhaps BINARY BACKUP or XLOG would be a better term, especially since this will probably have some overlap with streaming replication.

Likewise, if we segregate "DUMP" and BYPASSRLS then I think we need to call DUMP something else. Otherwise, it's a massive foot-gun; you get a "successful" backup only to find out it contains only a small part of the database.

My how this has become a can of worms...
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Stephen Frost
sfrost@snowman.net
In reply to: Jim Nasby (#47)
Re: Additional role attributes && superuser review

Jim,

* Jim Nasby (Jim.Nasby@BlueTreble.com) wrote:

On 12/29/14, 4:01 PM, Stephen Frost wrote:

That said, a 'DUMP' privilege which allows the user to dump the contents
of the entire database is entirely reasonable. We need to be clear in
the documentation though- such a 'DUMP' privilege is essentially
granting USAGE on all schemas and table-level SELECT on all tables and
sequences (anything else..?). To be clear, a user with this privilege
can utilize that access without using pg_dump.

Yeah... it may be better to call this something other than DUMP (see below).

Did you favor something else below..? I don't see it.

One other point is that this shouldn't imply any other privileges, imv.
I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things
should work 'sanely' with any combination of the two options.

That does violate POLA, but it's probably worth doing so...

That really depends on your view of the privilege. I'm inclined to view
it from the more-tightly-constrained point of view which matches up with
what I anticipate it actually providing. That doesn't translate into a
short name very well though, unfortunately.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

My specific concern about DUMP vs read all/only is IIRC dump needs to do more extensive locking than regular selects do, which can cause production problems.

The locks aren't any more extensive than regular selects, it's just that
the entire pg_dump works inside of one large transaction which lasts a
good long time and simply that can cause issues with high-rate update
systems.

There is one issue that occurs to me, however. We're talking about
pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Ugh, hadn't thought about that. :(

I don't think it kills the whole idea, but we do need to work out how to
address it.

* BACKUP - allows role to perform backup operations (as originally proposed)
* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations
* DUMP - allows role to perform pg_dump* backups of whole database
* MONITOR - allows role to view pg_stat_* details (as originally proposed)
* PROCSIGNAL - allows role to signal backend processes (as originally
proposed)well

Given the confusion that can exist with the data reading stuff, perhaps BINARY BACKUP or XLOG would be a better term, especially since this will probably have some overlap with streaming replication.

I don't really like 'xlog' as a permission. BINARY BACKUP is
interesting but if we're going to go multi-word, I would think we would
do PITR BACKUP or something FILESYSTEM BACKUP or similar. I'm not
really a big fan of the two-word options though.

Likewise, if we segregate "DUMP" and BYPASSRLS then I think we need to call DUMP something else. Otherwise, it's a massive foot-gun; you get a "successful" backup only to find out it contains only a small part of the database.

That's actually already dealt with. pg_dump defaults to setting the
row_security GUC to 'off', in which case you'll get an error if you hit
a table that has RLS enabled and you don't have BYPASSRLS. If you're
not checking for errors from pg_dump, well, there's a lot of ways you
could run into problems.

My how this has become a can of worms...

I'm not sure it's as bad as all that, but it does require some
thinking..

Thanks,

Stephen

#49Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Stephen Frost (#48)
Re: Additional role attributes && superuser review

On 12/29/14, 7:22 PM, Stephen Frost wrote:

One other point is that this shouldn't imply any other privileges, imv.

I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things
should work 'sanely' with any combination of the two options.

That does violate POLA, but it's probably worth doing so...

That really depends on your view of the privilege. I'm inclined to view
it from the more-tightly-constrained point of view which matches up with
what I anticipate it actually providing. That doesn't translate into a
short name very well though, unfortunately.

READ MOST? ;)

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

My specific concern about DUMP vs read all/only is IIRC dump needs to do more extensive locking than regular selects do, which can cause production problems.

The locks aren't any more extensive than regular selects, it's just that
the entire pg_dump works inside of one large transaction which lasts a
good long time and simply that can cause issues with high-rate update
systems.

Good, so really DUMP and READ ALL are the same.

There is one issue that occurs to me, however. We're talking about
pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Ugh, hadn't thought about that.:(

I don't think it kills the whole idea, but we do need to work out how to
address it.

Yeah... it does indicate that we shouldn't call this thing DUMP, but perhaps as long as we put appropriate HINTS in the error paths that pg_dumpall would hit it's OK to call it DUMP. (My specific concern is it's natural to assume that DUMP would include pg_dumpall).

Given the confusion that can exist with the data reading stuff, perhaps BINARY BACKUP or XLOG would be a better term, especially since this will probably have some overlap with streaming replication.

I don't really like 'xlog' as a permission. BINARY BACKUP is
interesting but if we're going to go multi-word, I would think we would
do PITR BACKUP or something FILESYSTEM BACKUP or similar. I'm not
really a big fan of the two-word options though.

BINARY? Though that's pretty generic. STREAM might be better, even though it's not exactly accurate for a simple backup.

Perhaps this is just a documentation issue, but there's enough caveats floating around here that I'm reluctant to rely on that.

Likewise, if we segregate "DUMP" and BYPASSRLS then I think we need to call DUMP something else. Otherwise, it's a massive foot-gun; you get a "successful" backup only to find out it contains only a small part of the database.

That's actually already dealt with. pg_dump defaults to setting the
row_security GUC to 'off', in which case you'll get an error if you hit
a table that has RLS enabled and you don't have BYPASSRLS. If you're
not checking for errors from pg_dump, well, there's a lot of ways you
could run into problems.

This also indicates DUMP is better than something like READ ALL, if we can handle the pg_dumpall case.

Based on all this, my inclination is DUMP with appropriate hints for pg_dumpall, and BINARY.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Amit Kapila
amit.kapila16@gmail.com
In reply to: Stephen Frost (#48)
Re: Additional role attributes && superuser review

On Tue, Dec 30, 2014 at 6:52 AM, Stephen Frost <sfrost@snowman.net> wrote:

There is one issue that occurs to me, however. We're talking about
pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Ugh, hadn't thought about that. :(

I don't think it kills the whole idea, but we do need to work out how to
address it.

One way to address this might be to allow this only for superusers,
another could be have a separate privilege (DBA) or a role which is
not a superuser, however can be used to perform such tasks.

* BACKUP - allows role to perform backup operations (as originally

proposed)

* LOG - allows role to rotate log files - remains broad enough to

consider

future log related operations
* DUMP - allows role to perform pg_dump* backups of whole database
* MONITOR - allows role to view pg_stat_* details (as originally

proposed)

* PROCSIGNAL - allows role to signal backend processes (as originally
proposed)well

Given the confusion that can exist with the data reading stuff, perhaps

BINARY BACKUP or XLOG would be a better term, especially since this will
probably have some overlap with streaming replication.

I don't really like 'xlog' as a permission. BINARY BACKUP is
interesting but if we're going to go multi-word, I would think we would
do PITR BACKUP or something FILESYSTEM BACKUP or similar. I'm not
really a big fan of the two-word options though.

How about PHYSICAL BACKUP (for basebackup) and LOGICAL BACKUP
for pg_dump?

This is normally the terminology I have seen people using for these
backup's.

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#51Stephen Frost
sfrost@snowman.net
In reply to: Amit Kapila (#50)
Re: Additional role attributes && superuser review

* Amit Kapila (amit.kapila16@gmail.com) wrote:

On Tue, Dec 30, 2014 at 6:52 AM, Stephen Frost <sfrost@snowman.net> wrote:

There is one issue that occurs to me, however. We're talking about
pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Ugh, hadn't thought about that. :(

I don't think it kills the whole idea, but we do need to work out how to
address it.

One way to address this might be to allow this only for superusers,

Huh? The point here is to have a role account which isn't a superuser
who can perform these actions.

another could be have a separate privilege (DBA) or a role which is
not a superuser, however can be used to perform such tasks.

I'm pretty sure we've agreed that having a catch-all role attribute like
'DBA' is a bad idea because we'd likely be adding privileges to it later
which would expand the rights of users with that attribute, possibly
beyond what is intended.

I don't really like 'xlog' as a permission. BINARY BACKUP is
interesting but if we're going to go multi-word, I would think we would
do PITR BACKUP or something FILESYSTEM BACKUP or similar. I'm not
really a big fan of the two-word options though.

How about PHYSICAL BACKUP (for basebackup) and LOGICAL BACKUP
for pg_dump?

Again, this makes it look like the read-all right would be specific to
users executing pg_dump, which is incorrect and misleading. "PHYSICAL"
might also imply the ability to do pg_basebackup.

This is normally the terminology I have seen people using for these
backup's.

I do like the idea of trying to find a correlation in the documentation
for these rights, though I'm not sure we'll be able to.

Thanks,

Stephen

#52Magnus Hagander
magnus@hagander.net
In reply to: Stephen Frost (#46)
Re: Additional role attributes && superuser review

On Mon, Dec 29, 2014 at 11:01 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

I'd suggest it's called DUMP if that's what it allows, to keep it

separate

from the backup parts.

Makes sense to me.

I'm fine calling it 'DUMP', but for different reasons.

We have no (verifiable) idea what client program is being used to
connect and therefore we shouldn't try to tie the name of the client
program to the permission.

That said, a 'DUMP' privilege which allows the user to dump the contents
of the entire database is entirely reasonable. We need to be clear in
the documentation though- such a 'DUMP' privilege is essentially
granting USAGE on all schemas and table-level SELECT on all tables and

sequences (anything else..?). To be clear, a user with this privilege

can utilize that access without using pg_dump.

Well, it would not give you full USAGE - granted USAGE on a schema, you can
execute functions in there for example (provided permissions). This
privilege would not do that, it would only give you COPY access. (And also
COPY != SELECT in the way that the rule system applies, I think? And this
one could be for COPY only)

But other than that I agree - for *all* these privileges, it needs to be
clearly documented that they are not limited to a specific client side
application, even if their name happens to be similar to one.

One other point is that this shouldn't imply any other privileges, imv.

I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things

I think BYPASSRLS would have to be implicitly granted by the DUMP
privilege. Without that, the DUMP privilege is more or less meaningless
(for anybody who uses RLS - but if they don't use RLS, then having it
include BYPASSRLS makes no difference). Worse than that, you may end up
with a dump that you cannot restore.

Similar concerns would exist for the existing REPLICATION role for example
- that one clearly lets you bypass RLS as well, just not with a SQL
statement.

should work 'sanely' with any combination of the two options.

Similairly, DUMP shouldn't imply BACKUP or visa-versa. In particular,
I'd like to see roles which have only the BACKUP privilege be unable to
directly read any data (modulo things granted to PUBLIC). This would
allow an unprivileged user to manage backups, kick off ad-hoc ones, etc,
without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but that's
entirely possible with more advanced SANs and enterprise backup
solutions).

So you're saying a privilege that would allow you to do
pg_start_backup()/pg_stop_backup() but *not* actually use pg_basebackup?
That would be "EXCLUSIVEBACKUP" or something like that, to be consistent
with existing terminology though.

That seems really bad names, IMHO. Why? Because we use WAL and XLOG

throughout documentation and parameters and code to mean *the same

thing*.

And here they'd suddenly mean different things. If we need them as

separate

privileges, I think we need much better names. (And a better

description -

what is "xlog operations" really?)

Fair enough, ultimately what I was trying to address is the following
concern raised by Alvaro:

"To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say."

Note that the BACKUP role attribute was never intended to cover the
pg_dump use-case. Simply the name of it caused confusion though. I'm
not sure if adding a DUMP role attribute is sufficient enough to address
that confusion, but I haven't got a better idea either.

We need to separate the logical backups (pg_dump) from the physical ones
(start/stop+filesystem and pg_basebackup). We might also need to separate
the two different ways of doing physical backups.

Personalyl I think using the DUMP name makes that a lot more clear. Maybe
we need to avoid using BACKUP alone as well, to make sure it doesn't go the
other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two different
ones perhaps?

When indeed, what it meant was to have the following separate (effectively

merging #2 and #3):

1) ability to pg_dump
2) ability to start/stop backups *and* ability to execute xlog related
functions.

We probably also need to define what those "xlog related functions"
actually arse. pg_current_xlog_location() is definitely an xlog related
function, but does it need the privilege? pg_switch_xlog()?
pg_start_backup()? pg_xlog_replay_pause()?

I think just calling them "xlog related functions" is doing us a disservice
there. Definitely once we have an actual documentation to write for it, but
also in this discussion.

That sounds reasonable to me (and is what was initially proposed, though

I've come around to the thinking that this BACKUP role attribute should
also allow pg_xlog_replay_pause/resume(), as those can be useful on
replicas).

If it's for replicas, then why are we not using the REPLICATION privilege
which is extremely similar to this?

Given this clarification:

I think #1 could certainly be answered by using DUMP. I have no strong
opinion in either direction, though I do think that BACKUP does make the
most sense for #2. Previously, Stephen had mentioned a READONLY

capability

that could effectively work for pg_dump, though, Jim's suggestion of
keeping 'read-all' separate from 'ability to pg_dump' seems logical. In
either case, I certainly wouldn't mind having a wider agreement/consensus
on this approach.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

You mean something that restricts the user to read even *if* write
permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

There is one issue that occurs to me, however. We're talking about

pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Well, from an actual security perspective that would make it equivalent to
superuser in the case of anybody using password auth. I'm not sure we cant
to grant that out to DUMP by default - perhaps we need a separate one for
DUMPAUTH or DUMPPASSWORDS.

(We could dump all the users *without* passwords with just the DUMP
privilege)

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#53Stephen Frost
sfrost@snowman.net
In reply to: Magnus Hagander (#52)
Re: Additional role attributes && superuser review

* Magnus Hagander (magnus@hagander.net) wrote:

On Mon, Dec 29, 2014 at 11:01 PM, Stephen Frost <sfrost@snowman.net> wrote:

That said, a 'DUMP' privilege which allows the user to dump the contents
of the entire database is entirely reasonable. We need to be clear in
the documentation though- such a 'DUMP' privilege is essentially
granting USAGE on all schemas and table-level SELECT on all tables and

sequences (anything else..?). To be clear, a user with this privilege

can utilize that access without using pg_dump.

Well, it would not give you full USAGE - granted USAGE on a schema, you can
execute functions in there for example (provided permissions). This
privilege would not do that,

The approach I was thinking was to document and implement this as
impliciting granting exactly USAGE and SELECT rights, no more (not
BYPASSRLS) and no less (yes, the role could execute functions). I agree
that doing so would be strictly more than what pg_dump actually requires
but it's also what we actually have support for in our privilege system.

it would only give you COPY access. (And also
COPY != SELECT in the way that the rule system applies, I think? And this
one could be for COPY only)

COPY certainly does equal SELECT rights.. We haven't got an independent
COPY privilege and I don't think it makes sense to invent one. It
sounds like you're suggesting that we add a hack directly into COPY for
this privilege, but my thinking is that the right place to change for
this is in the privilege system and not hack it into individual
commands.. I'm also a bit nervous that we'll end up painting ourselves
into a corner if we hack this to mean exactly what pg_dump needs today.

Lastly, I've been considering other use-cases for this privilege beyond
the pg_dump one (thinking about auditing, for example).

But other than that I agree - for *all* these privileges, it needs to be
clearly documented that they are not limited to a specific client side
application, even if their name happens to be similar to one.

Right.

One other point is that this shouldn't imply any other privileges, imv.

I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things

I think BYPASSRLS would have to be implicitly granted by the DUMP
privilege. Without that, the DUMP privilege is more or less meaningless
(for anybody who uses RLS - but if they don't use RLS, then having it
include BYPASSRLS makes no difference). Worse than that, you may end up
with a dump that you cannot restore.

The dump would fail, as I mentioned before. I don't see why BYPASSRLS
has to be implicitly granted by the DUMP privilege and can absolutely
see use-cases for it to *not* be. Those use-cases would require passing
pg_dump the --enable-row-security option, but that's why it's there.

Implementations which don't use RLS are not impacted either way, so we
don't need to consider them. Implementations which *do* use RLS, in my
experience, would certainly understand and expect BYPASSRLS to be
required if they want this role to be able to dump all data out
regardless of the RLS policies. What does implicitly including
BYPASSRLS in this privilege gain us? A slightly less irritated DBA who
forgot to include BYPASSRLS and ended up getting a pg_dump error because
of it. On the other hand, it walls off any possibility of using this
privilege for roles who shouldn't be allowed to bypass RLS or for
pg_dumps to be done across the entire system for specific RLS policies.

Similar concerns would exist for the existing REPLICATION role for example
- that one clearly lets you bypass RLS as well, just not with a SQL
statement.

I'm not sure that I see the point you're making here. Yes, REPLICATION
allows you to do a filesystem copy of the entire database and that
clearly bypasses RLS and *all* of our privilege system. I'm suggesting
that this role attribute work as an implicit grant of privileges we
already have. That strikes me as easy to document and very clear for
users.

should work 'sanely' with any combination of the two options.

Similairly, DUMP shouldn't imply BACKUP or visa-versa. In particular,
I'd like to see roles which have only the BACKUP privilege be unable to
directly read any data (modulo things granted to PUBLIC). This would
allow an unprivileged user to manage backups, kick off ad-hoc ones, etc,
without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but that's
entirely possible with more advanced SANs and enterprise backup
solutions).

So you're saying a privilege that would allow you to do
pg_start_backup()/pg_stop_backup() but *not* actually use pg_basebackup?

Yes.

That would be "EXCLUSIVEBACKUP" or something like that, to be consistent
with existing terminology though.

Ok. I agree that working out the specific naming is difficult and would
like to focus on that, but we probably need to agree on the specific
capabilities first. :)

Fair enough, ultimately what I was trying to address is the following
concern raised by Alvaro:

"To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something other
than BACKUP; because later we will want the ability to grant someone the
ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say."

Note that the BACKUP role attribute was never intended to cover the
pg_dump use-case. Simply the name of it caused confusion though. I'm
not sure if adding a DUMP role attribute is sufficient enough to address
that confusion, but I haven't got a better idea either.

We need to separate the logical backups (pg_dump) from the physical ones
(start/stop+filesystem and pg_basebackup). We might also need to separate
the two different ways of doing physical backups.

Right, I view those as three distinct types of privileges (see below).

Personalyl I think using the DUMP name makes that a lot more clear. Maybe
we need to avoid using BACKUP alone as well, to make sure it doesn't go the
other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two different
ones perhaps?

DUMP - implicitly grants existing rights, to facilitate pg_dump and
perhaps some other use-cases
BASEBACKUP - allows pg_basebackup, which means bypassing all in-DB
privilege systems
EXCLUSIVEBACKUP - just allows pg_start/stop_backup and friends

I'm still not entirely happy with the naming, but I feel like we're
getting there. One thought I had was using ARCHIVE somewhere, but I
kind of like BASEBACKUP meaning what's needed to run pg_basebackup, and,
well, we say 'backup' in the pg_start/stop_backup function names, so
it's kind of hard to avoid that. EXCLUSIVEBACKUP seems really long tho.

When indeed, what it meant was to have the following separate (effectively

merging #2 and #3):

1) ability to pg_dump
2) ability to start/stop backups *and* ability to execute xlog related
functions.

We probably also need to define what those "xlog related functions"
actually arse. pg_current_xlog_location() is definitely an xlog related
function, but does it need the privilege? pg_switch_xlog()?
pg_start_backup()? pg_xlog_replay_pause()?

I had defined them when I started the thread:

pg_start_backup
pg_stop_backup
pg_switch_xlog
pg_create_restore_point

Later I added:

pg_xlog_replay_pause
pg_xlog_replay_resume

Though I'd be find if the xlog_replay ones were their own privilege (eg:
REPLAY or something).

I think just calling them "xlog related functions" is doing us a disservice
there. Definitely once we have an actual documentation to write for it, but
also in this discussion.

It wasn't my intent to be ambiguous about it, I just wasn't repeating
the list with each post. The discussion starts here:

20141015052259.GG28859@tamriel.snowman.net

That sounds reasonable to me (and is what was initially proposed, though

I've come around to the thinking that this BACKUP role attribute should
also allow pg_xlog_replay_pause/resume(), as those can be useful on
replicas).

If it's for replicas, then why are we not using the REPLICATION privilege
which is extremely similar to this?

I don't actually see REPLICATION as being the same.

The point is to have a role which can log into the replica, pause
the stream to be able to run whatever queries they're permitted to
(which might not include all of the data) and then resume it when done.
Perhaps that needs to be independent of the EXCLUSIVEBACKUP role, but
it's definitely different from the REPLICATION privilege.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

You mean something that restricts the user to read even *if* write
permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

There is one issue that occurs to me, however. We're talking about

pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Well, from an actual security perspective that would make it equivalent to
superuser in the case of anybody using password auth. I'm not sure we cant
to grant that out to DUMP by default - perhaps we need a separate one for
DUMPAUTH or DUMPPASSWORDS.

That makes sense to me- DUMPAUTH or maybe just DUMPALL (to go with
pg_dumpall).

(We could dump all the users *without* passwords with just the DUMP
privilege)

Agreed. That's actually something that I think would be *really* nice-
an option to dump the necessary globals for whatever database you're
running pg_dump against. We have existing problems in this area
(database-level GUCs aren't considered database-specific and therefore
aren't picked up by pg_dump, for example..), but being able to also dump
the roles which are used in a given database with pg_dump would be
*really* nice..

Thanks,

Stephen

#54Amit Kapila
amit.kapila16@gmail.com
In reply to: Stephen Frost (#51)
Re: Additional role attributes && superuser review

On Tue, Dec 30, 2014 at 6:35 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Amit Kapila (amit.kapila16@gmail.com) wrote:

On Tue, Dec 30, 2014 at 6:52 AM, Stephen Frost <sfrost@snowman.net>

wrote:

another could be have a separate privilege (DBA) or a role which is
not a superuser, however can be used to perform such tasks.

I'm pretty sure we've agreed that having a catch-all role attribute like
'DBA' is a bad idea because we'd likely be adding privileges to it later
which would expand the rights of users with that attribute, possibly
beyond what is intended.

Yes, that could happen but do you want to say that is the only reason
to consider server level roles (such as DBA) a bad idea. Can't we make
users aware of such things via documentation and then there will be
some onus on user's to give such a privilege with care.

On looking around, it seems many of the databases provides such
roles
https://dbbulletin.wordpress.com/2013/05/29/backup-privileges-needed-to-backup-databases/

In the link, though they are talking about physical (file level) backup,
there is mention about such roles in other databases.

The other way as discussed on this thread is to use something like
DUMPALL (or DUMPFULL) privilege which also sounds to be a good
way, apart from the fact that the same privilege can be used for
non-dump purposes to extract the information from database/cluster.

I don't really like 'xlog' as a permission. BINARY BACKUP is
interesting but if we're going to go multi-word, I would think we

would

do PITR BACKUP or something FILESYSTEM BACKUP or similar. I'm not
really a big fan of the two-word options though.

How about PHYSICAL BACKUP (for basebackup) and LOGICAL BACKUP
for pg_dump?

Again, this makes it look like the read-all right would be specific to
users executing pg_dump, which is incorrect and misleading. "PHYSICAL"
might also imply the ability to do pg_basebackup.

That's right, however having unrelated privileges for doing physical
backup via pg_basebackup and pg_start*/pg_stop* method also
doesn't sound to be the best way (can be slightly difficult for
users to correlate after reading docs). Don't you think in this case
we should have some form of hierarchy for privileges (like user
having privilege to do pg_basebackup can also perform the
backup via pg_start*/pg_stop* method or some other way to relate
both forms of physical backup)?

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#55José Luis Tallón
jltallon@adv-solutions.net
In reply to: Stephen Frost (#53)
Re: Additional role attributes && superuser review

On 12/30/2014 04:16 PM, Stephen Frost wrote:

[snip]
The approach I was thinking was to document and implement this as
impliciting granting exactly USAGE and SELECT rights, no more (not
BYPASSRLS) and no less (yes, the role could execute functions). I agree
that doing so would be strictly more than what pg_dump actually requires
but it's also what we actually have support for in our privilege system.

Hmmm.... coupled with your comments below, I'd say some tweaking of the
existing privileges is actually needed if we want to add these new
"capabilities".

BTW, and since this is getting a bit bigger than originally considered:
would it be interesting to support some extension-defined capabilities, too?
(for things can't be easily controlled by the existing USAGE /
SELECT / ... rights, I mean)

it would only give you COPY access. (And also
COPY != SELECT in the way that the rule system applies, I think? And this
one could be for COPY only)

COPY certainly does equal SELECT rights.. We haven't got an independent
COPY privilege and I don't think it makes sense to invent one.

FWIW, a COPY(DUMP?) privilege different from SELECT would make sense.
Considering your below comments it would be better that it not imply
BYPASSRLS, though.

It
sounds like you're suggesting that we add a hack directly into COPY for
this privilege, but my thinking is that the right place to change for
this is in the privilege system and not hack it into individual
commands.. I'm also a bit nervous that we'll end up painting ourselves
into a corner if we hack this to mean exactly what pg_dump needs today.

Agreed.

Lastly, I've been considering other use-cases for this privilege beyond
the pg_dump one (thinking about auditing, for example).

ISTR there was something upthread on an AUDIT role, right?
This might be the moment to add it....

[snip]

Similar concerns would exist for the existing REPLICATION role for example
- that one clearly lets you bypass RLS as well, just not with a SQL
statement.

I'm not sure that I see the point you're making here. Yes, REPLICATION
allows you to do a filesystem copy of the entire database and that
clearly bypasses RLS and *all* of our privilege system. I'm suggesting
that this role attribute work as an implicit grant of privileges we
already have. That strikes me as easy to document and very clear for
users.

+1

[snip]
So you're saying a privilege that would allow you to do
pg_start_backup()/pg_stop_backup() but *not* actually use pg_basebackup?
Yes.

That would be "EXCLUSIVEBACKUP" or something like that, to be consistent
with existing terminology though.

Ok. I agree that working out the specific naming is difficult and would
like to focus on that, but we probably need to agree on the specific
capabilities first. :)

Yes :)
For the record, LOGBACKUP vs PHYSBACKUP was suggested a couple days ago.
I'd favor DUMP(logical) and BACKUP(physical) --- for lack of a better name.
The above reasoning would have pg_basebackup requiring REPLICATION,
which is a logical consequence of the implementation but strikes me as a
bit surprising in the light of these other privileges.

[snip]

Personalyl I think using the DUMP name makes that a lot more clear. Maybe
we need to avoid using BACKUP alone as well, to make sure it doesn't go the
other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two different
ones perhaps?

DUMP - implicitly grants existing rights, to facilitate pg_dump and
perhaps some other use-cases
BASEBACKUP - allows pg_basebackup, which means bypassing all in-DB
privilege systems
EXCLUSIVEBACKUP - just allows pg_start/stop_backup and friends

I'm still not entirely happy with the naming, but I feel like we're
getting there. One thought I had was using ARCHIVE somewhere, but I
kind of like BASEBACKUP meaning what's needed to run pg_basebackup, and,
well, we say 'backup' in the pg_start/stop_backup function names, so
it's kind of hard to avoid that. EXCLUSIVEBACKUP seems really long tho.

ARCHIVE, though completely appropriate for the "exclusivebackup" case
above (so this would become DUMP/BASEBACKUP/ARCHIVE +REPLICATION) might
end up causing quite some confusion ... ("what? WAL archiving is NOT
granted by the "archive" privilege, but requires a superuser to turn it
on(via ALTER SYSTEM)?

POLA again....

I had defined them when I started the thread:

pg_start_backup
pg_stop_backup
pg_switch_xlog
pg_create_restore_point

... for "BACKUP" / EXCLUSIVEBACKUP (or, actually, FSBACKUP/PHYSBACKUP ...)

Later I added:

pg_xlog_replay_pause
pg_xlog_replay_resume

Though I'd be find if the xlog_replay ones were their own privilege (eg:
REPLAY or something).

+1

I think just calling them "xlog related functions" is doing us a disservice
there. Definitely once we have an actual documentation to write for it, but
also in this discussion.

[snip]

If it's for replicas, then why are we not using the REPLICATION privilege
which is extremely similar to this?

I don't actually see REPLICATION as being the same.

REPLICATION (ability to replicate) vs REPLICAOPERATOR (ability to
control *the replica* or the R/O behaviour of the server, for that
matter...)

The point is to have a role which can log into the replica, pause
the stream to be able to run whatever queries they're permitted to
(which might not include all of the data) and then resume it when done.
Perhaps that needs to be independent of the EXCLUSIVEBACKUP role, but
it's definitely different from the REPLICATION privilege.

Looks like it is.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for
role attributes, as I see it (see my comments above). That said, having
DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

You mean something that restricts the user to read even *if* write
permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

So "READONLY" ?

There is one issue that occurs to me, however. We're talking about

pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Well, from an actual security perspective that would make it equivalent to
superuser in the case of anybody using password auth. I'm not sure we cant
to grant that out to DUMP by default - perhaps we need a separate one for
DUMPAUTH or DUMPPASSWORDS.

That makes sense to me- DUMPAUTH or maybe just DUMPALL (to go with
pg_dumpall).

+1

(We could dump all the users *without* passwords with just the DUMP
privilege)

Agreed. That's actually something that I think would be *really* nice-
an option to dump the necessary globals for whatever database you're
running pg_dump against. We have existing problems in this area
(database-level GUCs aren't considered database-specific and therefore
aren't picked up by pg_dump, for example..), but being able to also dump
the roles which are used in a given database with pg_dump would be
*really* nice..

Ok, so this would imply modifying pg_dump to include database-level
configs. I would heartily welcome this.

So, it seems to me that we are actually evolving towards a set of
"low-level" "capabilities" and some "high-level" (use case-focused)
"privileges".
I am hereby volunteering to compile this thread into some wiki page. I'm
thinking "Privileges" as the title for starters. Suggestions?

Thanks,

/ J.L.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Magnus Hagander
magnus@hagander.net
In reply to: Stephen Frost (#53)
Re: Additional role attributes && superuser review

On Tue, Dec 30, 2014 at 4:16 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Magnus Hagander (magnus@hagander.net) wrote:

On Mon, Dec 29, 2014 at 11:01 PM, Stephen Frost <sfrost@snowman.net>

wrote:

That said, a 'DUMP' privilege which allows the user to dump the

contents

of the entire database is entirely reasonable. We need to be clear in
the documentation though- such a 'DUMP' privilege is essentially
granting USAGE on all schemas and table-level SELECT on all tables and

sequences (anything else..?). To be clear, a user with this privilege

can utilize that access without using pg_dump.

Well, it would not give you full USAGE - granted USAGE on a schema, you

can

execute functions in there for example (provided permissions). This
privilege would not do that,

The approach I was thinking was to document and implement this as
impliciting granting exactly USAGE and SELECT rights, no more (not
BYPASSRLS) and no less (yes, the role could execute functions). I agree

If the role could execute functions, it's *not* "exactly USAGE and SELECT
rights", it's now "USAGE and SELECT and EXECUTE" rights... Just to be
nitpicking, but see below.

that doing so would be strictly more than what pg_dump actually requires
but it's also what we actually have support for in our privilege system.

Yeah, but would it also be what people would actually *want*?

I think having it do exactly what pg_dump needs, and not things like
execute functions etc, would be the thing people want for a 'DUMP'
privilege.

We might *also* want a USAGEANDSELECTANDEXECUTEANDWHATNOTBUTNOBYPASSURL
(crap, it has to fit within NAMEDATALEN?) privilege, but I think that's a
different thing.

it would only give you COPY access. (And also

COPY != SELECT in the way that the rule system applies, I think? And this
one could be for COPY only)

COPY certainly does equal SELECT rights.. We haven't got an independent
COPY privilege and I don't think it makes sense to invent one. It

We don't, but I'm saying it might make sense to implement this. Not as a
regular privilige, but as a role attribute. I'm not sure it's the right
thing, but it might actually be interesting to entertain.

sounds like you're suggesting that we add a hack directly into COPY for
this privilege, but my thinking is that the right place to change for
this is in the privilege system and not hack it into individual
commands.. I'm also a bit nervous that we'll end up painting ourselves
into a corner if we hack this to mean exactly what pg_dump needs today.

One point would be that if we define it as "exactly what pg_dump needs",
that definition can change in a future major version.

One other point is that this shouldn't imply any other privileges, imv.

I'm specifically thinking of BYPASSRLS- that's independently grantable
and therefore should be explicitly set, if it's intended. Things

I think BYPASSRLS would have to be implicitly granted by the DUMP
privilege. Without that, the DUMP privilege is more or less meaningless
(for anybody who uses RLS - but if they don't use RLS, then having it
include BYPASSRLS makes no difference). Worse than that, you may end up
with a dump that you cannot restore.

The dump would fail, as I mentioned before. I don't see why BYPASSRLS
has to be implicitly granted by the DUMP privilege and can absolutely
see use-cases for it to *not* be. Those use-cases would require passing
pg_dump the --enable-row-security option, but that's why it's there.

So you're basically saying that if RLS is in use anywhere, this priviliege
alone would be useless, correct? And you would get a hard failure at *dump
time*, not at reload time? That would at least make it acceptable, as you
would know it's wrong. and we could make the error messages shown
specifically hint that you need to grant both privileges to make it useful.

We could/should also throw a WARNING if DUMP Is granted to a role without
BYPASSRLS in case row level security is enabled in the system, I think. But
that's more of an implementation detail.

Implementations which don't use RLS are not impacted either way, so we

don't need to consider them. Implementations which *do* use RLS, in my
experience, would certainly understand and expect BYPASSRLS to be
required if they want this role to be able to dump all data out
regardless of the RLS policies. What does implicitly including
BYPASSRLS in this privilege gain us? A slightly less irritated DBA who
forgot to include BYPASSRLS and ended up getting a pg_dump error because
of it. On the other hand, it walls off any possibility of using this
privilege for roles who shouldn't be allowed to bypass RLS or for
pg_dumps to be done across the entire system for specific RLS policies.

Yeah, I think that's acceptable as long as we get the error at dump, and
not at reload (when it's too late to fix it).

Similar concerns would exist for the existing REPLICATION role for example

- that one clearly lets you bypass RLS as well, just not with a SQL
statement.

I'm not sure that I see the point you're making here. Yes, REPLICATION
allows you to do a filesystem copy of the entire database and that
clearly bypasses RLS and *all* of our privilege system. I'm suggesting
that this role attribute work as an implicit grant of privileges we
already have. That strikes me as easy to document and very clear for
users.

It does - though documenting that it implicitly gives you a different
privilege as well is also easy :)

But it does potentially limit us to what we can actually do with the
priviliges. One reason to use them is exactly because we *cannot* express
this with our regular permissions (such as BYPASSRLS or REPLICATION).

For regular permissions, we could just pre-populate the system with
predefined roles and use regular GRANTs to those roles, instead of relying
on role attributes, which might in that case make it even more clear?

should work 'sanely' with any combination of the two options.

Similairly, DUMP shouldn't imply BACKUP or visa-versa. In particular,
I'd like to see roles which have only the BACKUP privilege be unable to
directly read any data (modulo things granted to PUBLIC). This would
allow an unprivileged user to manage backups, kick off ad-hoc ones,

etc,

without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but that's
entirely possible with more advanced SANs and enterprise backup
solutions).

So you're saying a privilege that would allow you to do
pg_start_backup()/pg_stop_backup() but *not* actually use pg_basebackup?

Yes.

What's really the usecase for that? I'd say that pg_start/stop backup is a
superset and a higher privilige than using pg_basebackup, since you can for
example destroy other peoples backups using it (by running pg_stop_backup
while they are running).

That would be "EXCLUSIVEBACKUP" or something like that, to be consistent

with existing terminology though.

Ok. I agree that working out the specific naming is difficult and would
like to focus on that, but we probably need to agree on the specific
capabilities first. :)

:)

Fair enough, ultimately what I was trying to address is the following

concern raised by Alvaro:

"To me, what this repeated discussion on this particular BACKUP point
says, is that the ability to run pg_start/stop_backend and the xlog
related functions should be a different privilege, i.e. something

other

than BACKUP; because later we will want the ability to grant someone

the

ability to run pg_dump on the whole database without being superuser,
and we will want to use the name BACKUP for that. So I'm inclined to
propose something more specific for this like WAL_CONTROL or
XLOG_OPERATOR, say."

Note that the BACKUP role attribute was never intended to cover the
pg_dump use-case. Simply the name of it caused confusion though. I'm
not sure if adding a DUMP role attribute is sufficient enough to

address

that confusion, but I haven't got a better idea either.

We need to separate the logical backups (pg_dump) from the physical ones
(start/stop+filesystem and pg_basebackup). We might also need to separate
the two different ways of doing physical backups.

Right, I view those as three distinct types of privileges (see below).

Personalyl I think using the DUMP name makes that a lot more clear. Maybe

we need to avoid using BACKUP alone as well, to make sure it doesn't go

the

other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two different
ones perhaps?

DUMP - implicitly grants existing rights, to facilitate pg_dump and
perhaps some other use-cases
BASEBACKUP - allows pg_basebackup, which means bypassing all in-DB
privilege systems

That's equivalent of REPLICATION, yes?

EXCLUSIVEBACKUP - just allows pg_start/stop_backup and friends

I'd say there is no "just" there really - that's a higher level privilege,
but I see your point :)

I'm still not entirely happy with the naming, but I feel like we're

getting there. One thought I had was using ARCHIVE somewhere, but I
kind of like BASEBACKUP meaning what's needed to run pg_basebackup, and,
well, we say 'backup' in the pg_start/stop_backup function names, so
it's kind of hard to avoid that. EXCLUSIVEBACKUP seems really long tho.

I don't like ARCHIVE in that it sounds like something to do with log
archiving. If anything, that would affect pg_receivexlog, but that's
definitely REPLICATION. I'd rather keep that one for a future time where we
might let users actually do something about log archiving directly, which
would then use the name.

The reason I like EXCLUSIVEBACKUP is that it's a term we already use. It's
only 4 chars longer than REPLICATION for example - does that really matter?
As long as we don't store the name, it should have no effect at all, and
even if we do, I'm not sure it matters. If you want it shorter, we can have
EXCLBACKUP :)

When indeed, what it meant was to have the following separate

(effectively

merging #2 and #3):

1) ability to pg_dump
2) ability to start/stop backups *and* ability to execute xlog

related

functions.

We probably also need to define what those "xlog related functions"
actually arse. pg_current_xlog_location() is definitely an xlog related
function, but does it need the privilege? pg_switch_xlog()?
pg_start_backup()? pg_xlog_replay_pause()?

I had defined them when I started the thread:

Apologies - I had missed that part.

pg_start_backup

pg_stop_backup
pg_switch_xlog
pg_create_restore_point

Ok. That makes sense.

Later I added:

pg_xlog_replay_pause
pg_xlog_replay_resume

Though I'd be find if the xlog_replay ones were their own privilege (eg:
REPLAY or something).

I think it makes more sense to have those replay functions to be a separate
privilege, yes. They have nothing to actually do with taking the backups -
they are for restoring them. And to restore the backups, you clearly
already have superuser level privileges on the system outside the db (at
least as long as we only allow full cluster restores).

That sounds reasonable to me (and is what was initially proposed, though

I've come around to the thinking that this BACKUP role attribute should
also allow pg_xlog_replay_pause/resume(), as those can be useful on
replicas).

If it's for replicas, then why are we not using the REPLICATION privilege
which is extremely similar to this?

I don't actually see REPLICATION as being the same.

The point is to have a role which can log into the replica, pause
the stream to be able to run whatever queries they're permitted to
(which might not include all of the data) and then resume it when done.
Perhaps that needs to be independent of the EXCLUSIVEBACKUP role, but
it's definitely different from the REPLICATION privilege.

Agreed per above, now that I realize what we mean. I do think it needs to
be at least as independent from EXCLUSIVEBACKUP as it does froM REPLICATION
though.

The read-all vs. ability-to-pg_dump distinction doesn't really exist for

role attributes, as I see it (see my comments above). That said,

having

DUMP or read-all is different from read-*only*, which would probably be
good to have independently. I can imagine a use-case for a read-only
account which only has read ability for those tables, schemas, etc,
explicitly granted to it.

You mean something that restricts the user to read even *if* write
permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

Agreed that this would be very useful.

There is one issue that occurs to me, however. We're talking about

pg_dump, but what about pg_dumpall? In particular, I don't think the
DUMP privilege should provide access to pg_authid, as that would allow
the user to bypass the privilege system in some environments by using
the hash to log in as a superuser. Now, I don't encourage using
password based authentication, especially for superuser accounts, but
lots of people do. The idea with these privileges is to allow certain
operations to be performed by a non-superuser while preventing trivial
access to superuser. Perhaps it's pie-in-the-sky, but my hope is to
achieve that.

Well, from an actual security perspective that would make it equivalent

to

superuser in the case of anybody using password auth. I'm not sure we

cant

to grant that out to DUMP by default - perhaps we need a separate one for
DUMPAUTH or DUMPPASSWORDS.

That makes sense to me- DUMPAUTH or maybe just DUMPALL (to go with
pg_dumpall).

I'd prefer DUMPAUTH specifically because it makes it glaringly obvious that
you're going to be dumping authentication data.

(We could dump all the users *without* passwords with just the DUMP

privilege)

Agreed. That's actually something that I think would be *really* nice-
an option to dump the necessary globals for whatever database you're
running pg_dump against. We have existing problems in this area
(database-level GUCs aren't considered database-specific and therefore
aren't picked up by pg_dump, for example..), but being able to also dump
the roles which are used in a given database with pg_dump would be
*really* nice..

Yes. Shouldn't be *that* hard, completely independent of this privilege
work. Famous last words, I'm sure...

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#57Stephen Frost
sfrost@snowman.net
In reply to: Magnus Hagander (#56)
Re: Additional role attributes && superuser review

* Magnus Hagander (magnus@hagander.net) wrote:

On Tue, Dec 30, 2014 at 4:16 PM, Stephen Frost <sfrost@snowman.net> wrote:

The approach I was thinking was to document and implement this as
impliciting granting exactly USAGE and SELECT rights, no more (not
BYPASSRLS) and no less (yes, the role could execute functions). I agree

If the role could execute functions, it's *not* "exactly USAGE and SELECT
rights", it's now "USAGE and SELECT and EXECUTE" rights... Just to be
nitpicking, but see below.

We seem to be coming at it from two different directions, so I'll try to
clarify. What I'm suggesting is that this role attribute be strictly
*additive*. Any other privileges granted to the role with this role
attribute would still be applicable, including grants made to PUBLIC.

This role attribute would *not* include EXECUTE rights, by that logic.
However, if PUBLIC was granted EXECUTE rights for the function, then
this role could execute that function.

What it sounds like is you're imagining this role attribute to mean the
role has *only* USAGE and SELECT (or COPY or whatever) rights across the
board and that any grants done explicitly to this role or to public
wouldn't be respected. In my view, that moves this away from a role
*attribute* to being a pre-defined *role*, as such a role would not be
usable for anything else.

that doing so would be strictly more than what pg_dump actually requires
but it's also what we actually have support for in our privilege system.

Yeah, but would it also be what people would actually *want*?

I can certainly see reasons why you'd want such a role to be able to
execute functions- in particular, consider xlog_pause anx xlog_resume.
If this role can't execute those functions then they probably can't
successfully run pg_dump against a replica.

I think having it do exactly what pg_dump needs, and not things like
execute functions etc, would be the thing people want for a 'DUMP'
privilege.

What if we want pg_dump in 9.6 to have an option to execute xlog_pause
and xlog_resume for you? You wouldn't be able to run that against a 9.5
database (or at least, that option wouldn't work).

We might *also* want a USAGEANDSELECTANDEXECUTEANDWHATNOTBUTNOBYPASSURL
(crap, it has to fit within NAMEDATALEN?) privilege, but I think that's a
different thing.

Our privilege system doesn't currently allow for negative grants (deny
user X the ability to run functions, even if EXECUTE is granted to
PUBLIC). I'm not against that idea, but I don't see it as the same as
this.

it would only give you COPY access. (And also

COPY != SELECT in the way that the rule system applies, I think? And this
one could be for COPY only)

COPY certainly does equal SELECT rights.. We haven't got an independent
COPY privilege and I don't think it makes sense to invent one. It

We don't, but I'm saying it might make sense to implement this. Not as a
regular privilige, but as a role attribute. I'm not sure it's the right
thing, but it might actually be interesting to entertain.

We've discussed having a role attribute for COPY-from-filesystem, but
pg_dump doesn't use that ever, it only uses COPY TO STDOUT. I'm not
a fan of making a COPY_TO_STDOUT-vs-SELECT distinction just for this..

sounds like you're suggesting that we add a hack directly into COPY for
this privilege, but my thinking is that the right place to change for
this is in the privilege system and not hack it into individual
commands.. I'm also a bit nervous that we'll end up painting ourselves
into a corner if we hack this to mean exactly what pg_dump needs today.

One point would be that if we define it as "exactly what pg_dump needs",
that definition can change in a future major version.

Sure, but that then gets a bit ugly because we encourage running the
latest version of pg_dump against the prior-major-version.

I think BYPASSRLS would have to be implicitly granted by the DUMP
privilege. Without that, the DUMP privilege is more or less meaningless
(for anybody who uses RLS - but if they don't use RLS, then having it
include BYPASSRLS makes no difference). Worse than that, you may end up
with a dump that you cannot restore.

The dump would fail, as I mentioned before. I don't see why BYPASSRLS
has to be implicitly granted by the DUMP privilege and can absolutely
see use-cases for it to *not* be. Those use-cases would require passing
pg_dump the --enable-row-security option, but that's why it's there.

So you're basically saying that if RLS is in use anywhere, this priviliege
alone would be useless, correct?

No, it would still be *extremely* useful. We have an option to pg_dump
to say "please dump with RLS enabled". What that means is that you'd be
able to dump the entire database for all data you're allowed to see
through RLS policies. How is that useless? I certainly think it'd be
very handy and a good way to get *segregated* dumps according to policy.

And you would get a hard failure at *dump
time*, not at reload time?

If you attempt to pg_dump a relation which has RLS enabled, and you
don't enable RLS with pg_dump, then you'll get a failure at dump time,
yes. That's far better than a reload-time failure.

That would at least make it acceptable, as you
would know it's wrong. and we could make the error messages shown
specifically hint that you need to grant both privileges to make it useful.

Agreed, having such a hint makes sense.

We could/should also throw a WARNING if DUMP Is granted to a role without
BYPASSRLS in case row level security is enabled in the system, I think. But
that's more of an implementation detail.

That's a bit ugly and RLS could be added to a relation after the DUMP
privilege is granted.

What I think this part of the discussion is getting at is that there's a
lot of people who view pg_dump as explicitly for "dump the ENTIRE
database". While that's one use-case it is certainly not the only one.
I find "pg_dump schema X" to be very common and often find that pg_dump
against an entire database isn't practical because of the size of the
database.

Implementations which don't use RLS are not impacted either way, so we

don't need to consider them. Implementations which *do* use RLS, in my
experience, would certainly understand and expect BYPASSRLS to be
required if they want this role to be able to dump all data out
regardless of the RLS policies. What does implicitly including
BYPASSRLS in this privilege gain us? A slightly less irritated DBA who
forgot to include BYPASSRLS and ended up getting a pg_dump error because
of it. On the other hand, it walls off any possibility of using this
privilege for roles who shouldn't be allowed to bypass RLS or for
pg_dumps to be done across the entire system for specific RLS policies.

Yeah, I think that's acceptable as long as we get the error at dump, and
not at reload (when it's too late to fix it).

Right, that's the same decision we came to in the general RLS
discussion.

Similar concerns would exist for the existing REPLICATION role for example

- that one clearly lets you bypass RLS as well, just not with a SQL
statement.

I'm not sure that I see the point you're making here. Yes, REPLICATION
allows you to do a filesystem copy of the entire database and that
clearly bypasses RLS and *all* of our privilege system. I'm suggesting
that this role attribute work as an implicit grant of privileges we
already have. That strikes me as easy to document and very clear for
users.

It does - though documenting that it implicitly gives you a different
privilege as well is also easy :)

But it does potentially limit us to what we can actually do with the
priviliges. One reason to use them is exactly because we *cannot* express
this with our regular permissions (such as BYPASSRLS or REPLICATION).

Ok, I see the point you're making that we could make this into a
capability which isn't something which can be expressed through our
existing GRANT system. That strikes me as a solution trying to find a
problem though. There's no need to invent such an oddity for this
particular use-case, I don't think.

For regular permissions, we could just pre-populate the system with
predefined roles and use regular GRANTs to those roles, instead of relying
on role attributes, which might in that case make it even more clear?

The reason for this approach is to address exactly the nightmare that is
trying to maintain those regular permissions across all the objects in
the system. Today, people simply give the role trying to do the pg_dump
superuser, which is the best option we have. Saying 'grant SELECT on
all the tables and USAGE on all the schemas' isn't suggested because
it's a huge pain. This role attribute provides a middle ground where
the pg_dump'ing role isn't a superuser, but you don't have to ensure
usage and select are granted to it for every relation.

should work 'sanely' with any combination of the two options.

Similairly, DUMP shouldn't imply BACKUP or visa-versa. In particular,
I'd like to see roles which have only the BACKUP privilege be unable to
directly read any data (modulo things granted to PUBLIC). This would
allow an unprivileged user to manage backups, kick off ad-hoc ones,

etc,

without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but that's
entirely possible with more advanced SANs and enterprise backup
solutions).

So you're saying a privilege that would allow you to do
pg_start_backup()/pg_stop_backup() but *not* actually use pg_basebackup?

Yes.

What's really the usecase for that? I'd say that pg_start/stop backup is a
superset and a higher privilige than using pg_basebackup, since you can for
example destroy other peoples backups using it (by running pg_stop_backup
while they are running).

One use-case is to allow unprivileged users to run adhoc backups.
Another is simply that you don't want the cronjob that runs the backup
to be able to read any of the data directly (why would you?). I agree
that the individuals who have this capability would still need to
coordinate or at lesat not step on each other or they could end up with
bad backups.

Another way to address that would be to require that the role calling
stop_backup be a member of the role which called start_backup (that also
address the superuser case, as superusers are considered members of all
roles). That seems like a pretty reasonable sanity check requirement.

Personalyl I think using the DUMP name makes that a lot more clear. Maybe

we need to avoid using BACKUP alone as well, to make sure it doesn't go

the

other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two different
ones perhaps?

DUMP - implicitly grants existing rights, to facilitate pg_dump and
perhaps some other use-cases
BASEBACKUP - allows pg_basebackup, which means bypassing all in-DB
privilege systems

That's equivalent of REPLICATION, yes?

Ah, yeah, seems like it would be. Got ahead of myself. :)

EXCLUSIVEBACKUP - just allows pg_start/stop_backup and friends

I'd say there is no "just" there really - that's a higher level privilege,
but I see your point :)

Well, see above, but ok.

I'm still not entirely happy with the naming, but I feel like we're

getting there. One thought I had was using ARCHIVE somewhere, but I
kind of like BASEBACKUP meaning what's needed to run pg_basebackup, and,
well, we say 'backup' in the pg_start/stop_backup function names, so
it's kind of hard to avoid that. EXCLUSIVEBACKUP seems really long tho.

I don't like ARCHIVE in that it sounds like something to do with log
archiving. If anything, that would affect pg_receivexlog, but that's
definitely REPLICATION. I'd rather keep that one for a future time where we
might let users actually do something about log archiving directly, which
would then use the name.

Ok.

The reason I like EXCLUSIVEBACKUP is that it's a term we already use. It's
only 4 chars longer than REPLICATION for example - does that really matter?

Eh, I guess not. :)

As long as we don't store the name, it should have no effect at all, and
even if we do, I'm not sure it matters. If you want it shorter, we can have
EXCLBACKUP :)

Eh.

I had defined them when I started the thread:

Apologies - I had missed that part.

No prob.

pg_start_backup

pg_stop_backup
pg_switch_xlog
pg_create_restore_point

Ok. That makes sense.

Later I added:

pg_xlog_replay_pause
pg_xlog_replay_resume

Though I'd be find if the xlog_replay ones were their own privilege (eg:
REPLAY or something).

I think it makes more sense to have those replay functions to be a separate
privilege, yes. They have nothing to actually do with taking the backups -
they are for restoring them. And to restore the backups, you clearly
already have superuser level privileges on the system outside the db (at
least as long as we only allow full cluster restores).

Not quite- remember that reply_pause/resume can be done on a replica, so
they are useful to be able to grant independent from superuser. Perhaps
we call such a role attribute XLOGREPLAY ?

I don't actually see REPLICATION as being the same.

The point is to have a role which can log into the replica, pause
the stream to be able to run whatever queries they're permitted to
(which might not include all of the data) and then resume it when done.
Perhaps that needs to be independent of the EXCLUSIVEBACKUP role, but
it's definitely different from the REPLICATION privilege.

Agreed per above, now that I realize what we mean. I do think it needs to
be at least as independent from EXCLUSIVEBACKUP as it does froM REPLICATION
though.

Ok.

You mean something that restricts the user to read even *if* write
permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

Agreed that this would be very useful.

Glad we agree.. Any thoughts on implementation? :)

Well, from an actual security perspective that would make it equivalent

to

superuser in the case of anybody using password auth. I'm not sure we

cant

to grant that out to DUMP by default - perhaps we need a separate one for
DUMPAUTH or DUMPPASSWORDS.

That makes sense to me- DUMPAUTH or maybe just DUMPALL (to go with
pg_dumpall).

I'd prefer DUMPAUTH specifically because it makes it glaringly obvious that
you're going to be dumping authentication data.

Ok.

(We could dump all the users *without* passwords with just the DUMP

privilege)

Agreed. That's actually something that I think would be *really* nice-
an option to dump the necessary globals for whatever database you're
running pg_dump against. We have existing problems in this area
(database-level GUCs aren't considered database-specific and therefore
aren't picked up by pg_dump, for example..), but being able to also dump
the roles which are used in a given database with pg_dump would be
*really* nice..

Yes. Shouldn't be *that* hard, completely independent of this privilege
work. Famous last words, I'm sure...

Indeed.

Thanks!

Stephen

#58Stephen Frost
sfrost@snowman.net
In reply to: Amit Kapila (#54)
Re: Additional role attributes && superuser review

Amit,

* Amit Kapila (amit.kapila16@gmail.com) wrote:

On Tue, Dec 30, 2014 at 6:35 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'm pretty sure we've agreed that having a catch-all role attribute like
'DBA' is a bad idea because we'd likely be adding privileges to it later
which would expand the rights of users with that attribute, possibly
beyond what is intended.

Yes, that could happen but do you want to say that is the only reason
to consider server level roles (such as DBA) a bad idea.

No, the other reason is that having more granular permissions is better
than catch-all's. 'superuser' is that catch-all today.

Can't we make
users aware of such things via documentation and then there will be
some onus on user's to give such a privilege with care.

Perhaps, but if we're going to go in that direction, I'd rather have a
hierarchy which is built upon more granular options rather than assuming
we know exactly what the 'DBA' role should have for every environment.

On looking around, it seems many of the databases provides such
roles
https://dbbulletin.wordpress.com/2013/05/29/backup-privileges-needed-to-backup-databases/

In the link, though they are talking about physical (file level) backup,
there is mention about such roles in other databases.

Most of this discussion is about non-physical-level-backups. We have
the REPLICATION role attribute for physical-level-backups and I don't
think anyone wants to get rid of that in favor of pulling it into some
'DBA' role attribute.

The other way as discussed on this thread is to use something like
DUMPALL (or DUMPFULL) privilege which also sounds to be a good
way, apart from the fact that the same privilege can be used for
non-dump purposes to extract the information from database/cluster.

I think we're probably going to go with DUMPAUTH as the specific role
attribute privilege, to make it clear that it includes authentication
information. That's really the only distinction between DUMP (or
whatever) and a privilege to support pg_dumpall.

This does make me think that we need to consider if this role attribute
implicitly grants CONNECT to all databases also.

How about PHYSICAL BACKUP (for basebackup) and LOGICAL BACKUP
for pg_dump?

Again, this makes it look like the read-all right would be specific to
users executing pg_dump, which is incorrect and misleading. "PHYSICAL"
might also imply the ability to do pg_basebackup.

That's right, however having unrelated privileges for doing physical
backup via pg_basebackup and pg_start*/pg_stop* method also
doesn't sound to be the best way (can be slightly difficult for
users to correlate after reading docs).

We should definitely segregate the privilege to run start/stop backup
and run pg_basebackup. One allows you to read the database files off
the filesystem more-or-less directly and the other doesn't. That's a
big difference.

Don't you think in this case
we should have some form of hierarchy for privileges (like user
having privilege to do pg_basebackup can also perform the
backup via pg_start*/pg_stop* method or some other way to relate
both forms of physical backup)?

If we had a specific privilege for pg_basebackup then I would agree that
it would allow call pg_start/stop_backup. The same is not true in
reverse though.

Thanks,

Stephen

#59Magnus Hagander
magnus@hagander.net
In reply to: Stephen Frost (#57)
Re: Additional role attributes && superuser review

On Wed, Dec 31, 2014 at 3:08 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Magnus Hagander (magnus@hagander.net) wrote:

On Tue, Dec 30, 2014 at 4:16 PM, Stephen Frost <sfrost@snowman.net>

wrote:

The approach I was thinking was to document and implement this as
impliciting granting exactly USAGE and SELECT rights, no more (not
BYPASSRLS) and no less (yes, the role could execute functions). I

agree

If the role could execute functions, it's *not* "exactly USAGE and SELECT
rights", it's now "USAGE and SELECT and EXECUTE" rights... Just to be
nitpicking, but see below.

We seem to be coming at it from two different directions, so I'll try to
clarify. What I'm suggesting is that this role attribute be strictly
*additive*. Any other privileges granted to the role with this role
attribute would still be applicable, including grants made to PUBLIC.

This role attribute would *not* include EXECUTE rights, by that logic.
However, if PUBLIC was granted EXECUTE rights for the function, then
this role could execute that function.

Ah, ok, mistunderstood that part.

What it sounds like is you're imagining this role attribute to mean the

role has *only* USAGE and SELECT (or COPY or whatever) rights across the
board and that any grants done explicitly to this role or to public
wouldn't be respected. In my view, that moves this away from a role
*attribute* to being a pre-defined *role*, as such a role would not be
usable for anything else.

No, i meant additive as well. I misread you.

that doing so would be strictly more than what pg_dump actually requires

but it's also what we actually have support for in our privilege

system.

Yeah, but would it also be what people would actually *want*?

I can certainly see reasons why you'd want such a role to be able to
execute functions- in particular, consider xlog_pause anx xlog_resume.

If this role can't execute those functions then they probably can't

successfully run pg_dump against a replica.

uh, pg_dump doesn't run those commands :P I don't see why that's a
requirement at all. And you can still always grant those things on top of
whatever the privilege gives you.

I think having it do exactly what pg_dump needs, and not things like

execute functions etc, would be the thing people want for a 'DUMP'
privilege.

What if we want pg_dump in 9.6 to have an option to execute xlog_pause
and xlog_resume for you? You wouldn't be able to run that against a 9.5
database (or at least, that option wouldn't work).

It would if you added an explicit grant for it, which would have to be
documented.

I don't see how that's different if the definition is "allows select on all
tables". That wouldn't automatically give it the ability to do xlog_pause
in an old version either.

We might *also* want a USAGEANDSELECTANDEXECUTEANDWHATNOTBUTNOBYPASSURL

(crap, it has to fit within NAMEDATALEN?) privilege, but I think that's a
different thing.

Our privilege system doesn't currently allow for negative grants (deny
user X the ability to run functions, even if EXECUTE is granted to
PUBLIC). I'm not against that idea, but I don't see it as the same as
this.

Agreed. That's what I said - different thing :)

it would only give you COPY access. (And also

COPY != SELECT in the way that the rule system applies, I think? And

this

one could be for COPY only)

COPY certainly does equal SELECT rights.. We haven't got an

independent

COPY privilege and I don't think it makes sense to invent one. It

We don't, but I'm saying it might make sense to implement this. Not as a
regular privilige, but as a role attribute. I'm not sure it's the right
thing, but it might actually be interesting to entertain.

We've discussed having a role attribute for COPY-from-filesystem, but
pg_dump doesn't use that ever, it only uses COPY TO STDOUT. I'm not
a fan of making a COPY_TO_STDOUT-vs-SELECT distinction just for this..

Yeah, it's probably going overboard with it, since AFAICT the only thing
that would actually be affected is RULEs on SELECT, which I bet most people
don't use on their tables.

sounds like you're suggesting that we add a hack directly into COPY for

this privilege, but my thinking is that the right place to change for
this is in the privilege system and not hack it into individual
commands.. I'm also a bit nervous that we'll end up painting ourselves
into a corner if we hack this to mean exactly what pg_dump needs today.

One point would be that if we define it as "exactly what pg_dump needs",
that definition can change in a future major version.

Sure, but that then gets a bit ugly because we encourage running the
latest version of pg_dump against the prior-major-version.

But the latest version of pg_dump *knows* how the prior major version
behaved with these things, and can either adapt, or give the user a message
about what they need to do to adapt.

I think BYPASSRLS would have to be implicitly granted by the DUMP

privilege. Without that, the DUMP privilege is more or less

meaningless

(for anybody who uses RLS - but if they don't use RLS, then having it
include BYPASSRLS makes no difference). Worse than that, you may end

up

with a dump that you cannot restore.

The dump would fail, as I mentioned before. I don't see why BYPASSRLS
has to be implicitly granted by the DUMP privilege and can absolutely
see use-cases for it to *not* be. Those use-cases would require

passing

pg_dump the --enable-row-security option, but that's why it's there.

So you're basically saying that if RLS is in use anywhere, this

priviliege

alone would be useless, correct?

No, it would still be *extremely* useful. We have an option to pg_dump
to say "please dump with RLS enabled". What that means is that you'd be
able to dump the entire database for all data you're allowed to see
through RLS policies. How is that useless? I certainly think it'd be
very handy and a good way to get *segregated* dumps according to policy.

Hmm. Yeah, I guess - my mindset was int he "database backup" mode, where a
"silently partial" backup is a really scary thing rather than a feature :)
I guess as long as you still dump all the parts, RLS itself ensures that
foreign keys etc will actually be valid upon a reaload?

And you would get a hard failure at *dump

time*, not at reload time?

If you attempt to pg_dump a relation which has RLS enabled, and you
don't enable RLS with pg_dump, then you'll get a failure at dump time,
yes. That's far better than a reload-time failure.

Good.

That would at least make it acceptable, as you

would know it's wrong. and we could make the error messages shown
specifically hint that you need to grant both privileges to make it

useful.

Agreed, having such a hint makes sense.

We could/should also throw a WARNING if DUMP Is granted to a role without
BYPASSRLS in case row level security is enabled in the system, I think.

But

that's more of an implementation detail.

That's a bit ugly and RLS could be added to a relation after the DUMP
privilege is granted.

Yes, it's not going to be all-covering, but it can still be a useful
hint/warning in the cases where it *does* that. We obviously still need
pg_dump to give the error in both scenarios.

What I think this part of the discussion is getting at is that there's a

lot of people who view pg_dump as explicitly for "dump the ENTIRE
database". While that's one use-case it is certainly not the only one.
I find "pg_dump schema X" to be very common and often find that pg_dump
against an entire database isn't practical because of the size of the
database.

Agreed. I was in that mindset since we were talking about other backups.
And FWIW, one reason I like to call it "DUMP" and not anything to do with
BACKUP is exactly that. pg_dump is often not very useful for backups
anymore, due to the size of databases, but it's very useful for other
things.

Similar concerns would exist for the existing REPLICATION role for

example

- that one clearly lets you bypass RLS as well, just not with a SQL
statement.

I'm not sure that I see the point you're making here. Yes, REPLICATION
allows you to do a filesystem copy of the entire database and that
clearly bypasses RLS and *all* of our privilege system. I'm suggesting
that this role attribute work as an implicit grant of privileges we
already have. That strikes me as easy to document and very clear for
users.

It does - though documenting that it implicitly gives you a different
privilege as well is also easy :)

But it does potentially limit us to what we can actually do with the
priviliges. One reason to use them is exactly because we *cannot* express
this with our regular permissions (such as BYPASSRLS or REPLICATION).

Ok, I see the point you're making that we could make this into a
capability which isn't something which can be expressed through our
existing GRANT system. That strikes me as a solution trying to find a
problem though. There's no need to invent such an oddity for this
particular use-case, I don't think.

Maybe not, but we should make sure we don't paint ourselves into a corner
where we cannot do it in the future either.

For regular permissions, we could just pre-populate the system with

predefined roles and use regular GRANTs to those roles, instead of

relying

on role attributes, which might in that case make it even more clear?

The reason for this approach is to address exactly the nightmare that is
trying to maintain those regular permissions across all the objects in
the system. Today, people simply give the role trying to do the pg_dump
superuser, which is the best option we have. Saying 'grant SELECT on
all the tables and USAGE on all the schemas' isn't suggested because
it's a huge pain. This role attribute provides a middle ground where
the pg_dump'ing role isn't a superuser, but you don't have to ensure
usage and select are granted to it for every relation.

No, what I'm saying is we could have *predefined role* that allows "select
on all the tables and usage on all the schemas". And you are unable to
actually remove that. It's not stored on the object, so you cannot REVOKE
the permission on the *object*. Since it's not store on the object it will
also automatically apply to all new objects, regardless of what you've done
with DEFAULT PRIVILEGES etc.

But you can grant users and other roles access to this role, and it's dealt
with like other roles from the "who has it" perspective, instead of being
special-cased.

Instead of "ALTER USER foo WITH DUMP" (or whatever), you'd just do a "GRANT
pgdump TO foo" (which would then also work through things like group
membership as well)

should work 'sanely' with any combination of the two options.

Similairly, DUMP shouldn't imply BACKUP or visa-versa. In

particular,

I'd like to see roles which have only the BACKUP privilege be

unable to

directly read any data (modulo things granted to PUBLIC). This

would

allow an unprivileged user to manage backups, kick off ad-hoc ones,

etc,

without being able to actually access any of the data (this would
require the actual backup system to have a similar control, but

that's

entirely possible with more advanced SANs and enterprise backup
solutions).

So you're saying a privilege that would allow you to do
pg_start_backup()/pg_stop_backup() but *not* actually use

pg_basebackup?

Yes.

What's really the usecase for that? I'd say that pg_start/stop backup is

a

superset and a higher privilige than using pg_basebackup, since you can

for

example destroy other peoples backups using it (by running pg_stop_backup
while they are running).

One use-case is to allow unprivileged users to run adhoc backups.

Another is simply that you don't want the cronjob that runs the backup

to be able to read any of the data directly (why would you?). I agree
that the individuals who have this capability would still need to
coordinate or at lesat not step on each other or they could end up with
bad backups.

Another way to address that would be to require that the role calling
stop_backup be a member of the role which called start_backup (that also
address the superuser case, as superusers are considered members of all
roles). That seems like a pretty reasonable sanity check requirement.

This diverges a bit from the actual role attribute discussion here, but I
think a better solution to this is to actually have a separate interface
rather than pg_start/pg_stop backup. One of the main problems with
pg_start/pg_stop vs pg_basebackup is that you can easily leave the system
in a broken state (for example by forgetting to pg_stop_backup, or by
accidentally pg_stop_backup:ing in the middle of someone elses backup).
pg_basebackup gets around this by automatically executing do_pg_stop_backup
if the client disconnects. This also lets us allow parallel base backups.
We could have an equivalent functionality exposed through a SQL function,
or argument to pg_start_backup - it would require the backup software to
keep the connection running as it works and then disconnect when it's done,
but for anything beyond the most trivial shellscript that's not exactly
hard, and it would make the backups safer.

If we did that, perhaps we don't even need a separate privilege for
pg_start_backup() as it is today, btu can leave that as superuser?

Personalyl I think using the DUMP name makes that a lot more clear.

Maybe

we need to avoid using BACKUP alone as well, to make sure it doesn't

go

the

other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two

different

ones perhaps?

DUMP - implicitly grants existing rights, to facilitate pg_dump and
perhaps some other use-cases
BASEBACKUP - allows pg_basebackup, which means bypassing all in-DB
privilege systems

That's equivalent of REPLICATION, yes?

Ah, yeah, seems like it would be. Got ahead of myself. :)

EXCLUSIVEBACKUP - just allows pg_start/stop_backup and friends

I'd say there is no "just" there really - that's a higher level

privilege,

but I see your point :)

Well, see above, but ok.

Later I added:

pg_xlog_replay_pause
pg_xlog_replay_resume

Though I'd be find if the xlog_replay ones were their own privilege

(eg:

REPLAY or something).

I think it makes more sense to have those replay functions to be a

separate

privilege, yes. They have nothing to actually do with taking the backups

-

they are for restoring them. And to restore the backups, you clearly
already have superuser level privileges on the system outside the db (at
least as long as we only allow full cluster restores).

Not quite- remember that reply_pause/resume can be done on a replica, so
they are useful to be able to grant independent from superuser. Perhaps
we call such a role attribute XLOGREPLAY ?

Yes, that's what I meant - they are useful, but they're not interesting for
the bacukp itself. So yes, something like XLOGREPLAY makes sense.

You mean something that restricts the user to read even *if* write

permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

Agreed that this would be very useful.

Glad we agree.. Any thoughts on implementation? :)

Where's your patch? :)

In theory we'd just have to trap any attempt to set the value for
transaction_read_only, no? Same as hot_standby does?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#60Stephen Frost
sfrost@snowman.net
In reply to: José Luis Tallón (#55)
Re: Additional role attributes && superuser review

José,

* José Luis Tallón (jltallon@adv-solutions.net) wrote:

On 12/30/2014 04:16 PM, Stephen Frost wrote:

The approach I was thinking was to document and implement this as
impliciting granting exactly USAGE and SELECT rights, no more (not
BYPASSRLS) and no less (yes, the role could execute functions). I agree
that doing so would be strictly more than what pg_dump actually requires
but it's also what we actually have support for in our privilege system.

Hmmm.... coupled with your comments below, I'd say some tweaking of
the existing privileges is actually needed if we want to add these
new "capabilities".

Not sure I see how..? Can you clarify what you think needs to be
changed in the existing privilege system?

BTW, and since this is getting a bit bigger than originally
considered: would it be interesting to support some
extension-defined capabilities, too?
(for things can't be easily controlled by the existing USAGE /
SELECT / ... rights, I mean)

Not entirely following what you mean here either, but to the extent that
it's independent from the current discussion, perhaps it deserves to be
on its own thread?

it would only give you COPY access. (And also
COPY != SELECT in the way that the rule system applies, I think? And this
one could be for COPY only)

COPY certainly does equal SELECT rights.. We haven't got an independent
COPY privilege and I don't think it makes sense to invent one.

FWIW, a COPY(DUMP?) privilege different from SELECT would make sense.
Considering your below comments it would be better that it not imply
BYPASSRLS, though.

How would a COPY-to-STDOUT privilege be different from SELECT?

Lastly, I've been considering other use-cases for this privilege beyond
the pg_dump one (thinking about auditing, for example).

ISTR there was something upthread on an AUDIT role, right?
This might be the moment to add it....

One of the challenges to adding such a role is defining what it means.
What privileges do you think such a role would have? I can see that
perhaps it would include read-only access to everything, but I'd want it
to also have read access to the logs..

That would be "EXCLUSIVEBACKUP" or something like that, to be consistent
with existing terminology though.

Ok. I agree that working out the specific naming is difficult and would
like to focus on that, but we probably need to agree on the specific
capabilities first. :)

Yes :)
For the record, LOGBACKUP vs PHYSBACKUP was suggested a couple days
ago. I'd favor DUMP(logical) and BACKUP(physical) --- for lack of a
better name.

I think I'm coming around to the EXCLUSIVEBACKUP option, per the
discussion with Magnus. I don't particularly like LOGBACKUP and don't
think it really makes sense, while PHYSBACKUP seems like it'd cover what
REPLICATION already does.

The above reasoning would have pg_basebackup requiring REPLICATION,
which is a logical consequence of the implementation but strikes me
as a bit surprising in the light of these other privileges.

I see what you mean that it's a bit strange for pg_basebackup to require
the REPLICATION role attribute, but, well, that's already the case, no?
Don't think we're going to change that..

ARCHIVE, though completely appropriate for the "exclusivebackup"
case above (so this would become DUMP/BASEBACKUP/ARCHIVE
+REPLICATION) might end up causing quite some confusion ... ("what?
WAL archiving is NOT granted by the "archive" privilege, but
requires a superuser to turn it on(via ALTER SYSTEM)?

Yeah, Magnus didn't like ARCHIVE either and I understand his reasoning.

pg_xlog_replay_pause
pg_xlog_replay_resume

Though I'd be find if the xlog_replay ones were their own privilege (eg:
REPLAY or something).

+1

Yeah, Magnus was also agreeing that they should be independent.

I think just calling them "xlog related functions" is doing us a disservice
there. Definitely once we have an actual documentation to write for it, but
also in this discussion.

[snip]

If it's for replicas, then why are we not using the REPLICATION privilege
which is extremely similar to this?

I don't actually see REPLICATION as being the same.

REPLICATION (ability to replicate) vs REPLICAOPERATOR (ability to
control *the replica* or the R/O behaviour of the server, for that
matter...)

Right, think Magnus and I clarified what was meant there.

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

So "READONLY" ?

Right.

Agreed. That's actually something that I think would be *really* nice-
an option to dump the necessary globals for whatever database you're
running pg_dump against. We have existing problems in this area
(database-level GUCs aren't considered database-specific and therefore
aren't picked up by pg_dump, for example..), but being able to also dump
the roles which are used in a given database with pg_dump would be
*really* nice..

Ok, so this would imply modifying pg_dump to include database-level
configs. I would heartily welcome this.

I think a lot of people would like to see that, though I do think it's
at least somewhat independent from this particular proposal.

So, it seems to me that we are actually evolving towards a set of
"low-level" "capabilities" and some "high-level" (use case-focused)
"privileges".

Well, I would argue that we're making a good set of 'low-level'
capabilities which users are then able to pull together as they see fit
into specific 'roles' for their environment. One environment might see
a DBA role as having X, Y, Z, while another only wants X and Y.

I am hereby volunteering to compile this thread into some wiki page.
I'm thinking "Privileges" as the title for starters. Suggestions?

A wiki page would certainly be useful, especially if it's done in such a
way that we can take and make it into the documentation to go along with
these role attributes easily.

Thanks!

Stephen

#61Stephen Frost
sfrost@snowman.net
In reply to: Magnus Hagander (#59)
Re: Additional role attributes && superuser review

* Magnus Hagander (magnus@hagander.net) wrote:

On Wed, Dec 31, 2014 at 3:08 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Magnus Hagander (magnus@hagander.net) wrote:

that doing so would be strictly more than what pg_dump actually requires

but it's also what we actually have support for in our privilege

system.

Yeah, but would it also be what people would actually *want*?

I can certainly see reasons why you'd want such a role to be able to
execute functions- in particular, consider xlog_pause anx xlog_resume.

If this role can't execute those functions then they probably can't

successfully run pg_dump against a replica.

uh, pg_dump doesn't run those commands :P I don't see why that's a
requirement at all. And you can still always grant those things on top of
whatever the privilege gives you.

Ok, so, first off, this is all about the discussion around "is this
additive or not".. Since we just agreed that it's additive, I'm not
sure I see the need to discuss the EXECUTE privileges..

Having said that though..

If you can't pause/resume then you can end up with your pg_dump
transaction getting killed. I'm aware of folks who already do this
today, by hand with shell scripts.. I agree that pg_dump doesn't do it,
but I do think it'd be nice to have and I can certainly see the use-case
for them..

I think having it do exactly what pg_dump needs, and not things like

execute functions etc, would be the thing people want for a 'DUMP'
privilege.

What if we want pg_dump in 9.6 to have an option to execute xlog_pause
and xlog_resume for you? You wouldn't be able to run that against a 9.5
database (or at least, that option wouldn't work).

It would if you added an explicit grant for it, which would have to be
documented.

Huh? An explicit grant for xlog_pause/xlog_resume won't work as we
check role attributes rights inside the function..

I don't see how that's different if the definition is "allows select on all
tables". That wouldn't automatically give it the ability to do xlog_pause
in an old version either.

Ok, that I agree with- you don't automatically get xlog_pause rights if
you have the 'select on all tables' right.

We've discussed having a role attribute for COPY-from-filesystem, but
pg_dump doesn't use that ever, it only uses COPY TO STDOUT. I'm not
a fan of making a COPY_TO_STDOUT-vs-SELECT distinction just for this..

Yeah, it's probably going overboard with it, since AFAICT the only thing
that would actually be affected is RULEs on SELECT, which I bet most people
don't use on their tables.

Well, we could make SELECT not work, but if you've got COPY then you can
still get all the data, so, yeah, not much different. I seriously doubt
many people are using rules..

One point would be that if we define it as "exactly what pg_dump needs",
that definition can change in a future major version.

Sure, but that then gets a bit ugly because we encourage running the
latest version of pg_dump against the prior-major-version.

But the latest version of pg_dump *knows* how the prior major version
behaved with these things, and can either adapt, or give the user a message
about what they need to do to adapt.

Yes, that's true.

No, it would still be *extremely* useful. We have an option to pg_dump
to say "please dump with RLS enabled". What that means is that you'd be
able to dump the entire database for all data you're allowed to see
through RLS policies. How is that useless? I certainly think it'd be
very handy and a good way to get *segregated* dumps according to policy.

Hmm. Yeah, I guess - my mindset was int he "database backup" mode, where a
"silently partial" backup is a really scary thing rather than a feature :)

Agreed, we don't ever want that.

I guess as long as you still dump all the parts, RLS itself ensures that
foreign keys etc will actually be valid upon a reaload?

If you set up your policies correctly, yes. RLS is flexible enough that
you could create policies which fail, but you have the same problem
today to some extent (consider tables you don't have SELECT rights on).

We could/should also throw a WARNING if DUMP Is granted to a role without
BYPASSRLS in case row level security is enabled in the system, I think.

But

that's more of an implementation detail.

That's a bit ugly and RLS could be added to a relation after the DUMP
privilege is granted.

Yes, it's not going to be all-covering, but it can still be a useful
hint/warning in the cases where it *does* that. We obviously still need
pg_dump to give the error in both scenarios.

I'm not against doing it, personally, but I suspect others won't like it
(or at least, that's been the case in the past with other things..).

What I think this part of the discussion is getting at is that there's a

lot of people who view pg_dump as explicitly for "dump the ENTIRE
database". While that's one use-case it is certainly not the only one.
I find "pg_dump schema X" to be very common and often find that pg_dump
against an entire database isn't practical because of the size of the
database.

Agreed. I was in that mindset since we were talking about other backups.
And FWIW, one reason I like to call it "DUMP" and not anything to do with
BACKUP is exactly that. pg_dump is often not very useful for backups
anymore, due to the size of databases, but it's very useful for other
things.

Ok.

Ok, I see the point you're making that we could make this into a
capability which isn't something which can be expressed through our
existing GRANT system. That strikes me as a solution trying to find a
problem though. There's no need to invent such an oddity for this
particular use-case, I don't think.

Maybe not, but we should make sure we don't paint ourselves into a corner
where we cannot do it in the future either.

Agreed. Do you see a risk here of that?

For regular permissions, we could just pre-populate the system with

predefined roles and use regular GRANTs to those roles, instead of

relying

on role attributes, which might in that case make it even more clear?

The reason for this approach is to address exactly the nightmare that is
trying to maintain those regular permissions across all the objects in
the system. Today, people simply give the role trying to do the pg_dump
superuser, which is the best option we have. Saying 'grant SELECT on
all the tables and USAGE on all the schemas' isn't suggested because
it's a huge pain. This role attribute provides a middle ground where
the pg_dump'ing role isn't a superuser, but you don't have to ensure
usage and select are granted to it for every relation.

No, what I'm saying is we could have *predefined role* that allows "select
on all the tables and usage on all the schemas". And you are unable to
actually remove that. It's not stored on the object, so you cannot REVOKE
the permission on the *object*. Since it's not store on the object it will
also automatically apply to all new objects, regardless of what you've done
with DEFAULT PRIVILEGES etc.

But you can grant users and other roles access to this role, and it's dealt
with like other roles from the "who has it" perspective, instead of being
special-cased.

Instead of "ALTER USER foo WITH DUMP" (or whatever), you'd just do a "GRANT
pgdump TO foo" (which would then also work through things like group
membership as well)

There's a definite backwards-compatibility concern with this, of course,
but I see where you're coming from. This only really applies with this
particular pg_dump-related role-attribute discussion though, right?

This role wouldn't be able to be logged in with, I presume? Would it
show up when you run \du? What about in pg_authid? I feel like it
would have to and I worry that users might not care for it- and what
happens if they want to remove it?

The question about role-attribute vs. inheirited-right is one that I've
been wondering about also though.

Another is simply that you don't want the cronjob that runs the backup

to be able to read any of the data directly (why would you?). I agree
that the individuals who have this capability would still need to
coordinate or at lesat not step on each other or they could end up with
bad backups.

Another way to address that would be to require that the role calling
stop_backup be a member of the role which called start_backup (that also
address the superuser case, as superusers are considered members of all
roles). That seems like a pretty reasonable sanity check requirement.

This diverges a bit from the actual role attribute discussion here, but I
think a better solution to this is to actually have a separate interface
rather than pg_start/pg_stop backup. One of the main problems with
pg_start/pg_stop vs pg_basebackup is that you can easily leave the system
in a broken state (for example by forgetting to pg_stop_backup, or by
accidentally pg_stop_backup:ing in the middle of someone elses backup).

I agree. I've had this happen to me a number of times and it really
stinks to have your *next* backup fail because the last one failed
half-way and didn't run pg_stop_backup.

pg_basebackup gets around this by automatically executing do_pg_stop_backup
if the client disconnects. This also lets us allow parallel base backups.
We could have an equivalent functionality exposed through a SQL function,
or argument to pg_start_backup - it would require the backup software to
keep the connection running as it works and then disconnect when it's done,
but for anything beyond the most trivial shellscript that's not exactly
hard, and it would make the backups safer.

Agreed, I do like that idea.

If we did that, perhaps we don't even need a separate privilege for
pg_start_backup() as it is today, btu can leave that as superuser?

I don't see how this follows though.. We are not talking about only
pg_basebackup or only about where the user running pg_start/stop
has filesystem-level access to the database.

Not quite- remember that reply_pause/resume can be done on a replica, so
they are useful to be able to grant independent from superuser. Perhaps
we call such a role attribute XLOGREPLAY ?

Yes, that's what I meant - they are useful, but they're not interesting for
the bacukp itself. So yes, something like XLOGREPLAY makes sense.

Ok, that works for me.

You mean something that restricts the user to read even *if* write

permissions has been granted on an individual table? Yeah, that would
actually be quite useful, I think - sort of a "reverse privilege".

Yes. My thinking on how to approach this was to forcibly set all of
their transactions to read-only.

Agreed that this would be very useful.

Glad we agree.. Any thoughts on implementation? :)

Where's your patch? :)

Haha. :P

In theory we'd just have to trap any attempt to set the value for
transaction_read_only, no? Same as hot_standby does?

I like the idea.. Will have to give it a shot and see what happens. :)

Thanks!

Stephen

#62Magnus Hagander
magnus@hagander.net
In reply to: Stephen Frost (#61)
Re: Additional role attributes && superuser review

On Wed, Dec 31, 2014 at 4:23 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Magnus Hagander (magnus@hagander.net) wrote:

On Wed, Dec 31, 2014 at 3:08 PM, Stephen Frost <sfrost@snowman.net>

wrote:

* Magnus Hagander (magnus@hagander.net) wrote:
I think having it do exactly what pg_dump needs, and not things like

execute functions etc, would be the thing people want for a 'DUMP'
privilege.

What if we want pg_dump in 9.6 to have an option to execute xlog_pause
and xlog_resume for you? You wouldn't be able to run that against a

9.5

database (or at least, that option wouldn't work).

It would if you added an explicit grant for it, which would have to be
documented.

Huh? An explicit grant for xlog_pause/xlog_resume won't work as we
check role attributes rights inside the function..

Correct, of course. I was confusing myself.

We've discussed having a role attribute for COPY-from-filesystem, but

pg_dump doesn't use that ever, it only uses COPY TO STDOUT. I'm not
a fan of making a COPY_TO_STDOUT-vs-SELECT distinction just for this..

Yeah, it's probably going overboard with it, since AFAICT the only thing
that would actually be affected is RULEs on SELECT, which I bet most

people

don't use on their tables.

Well, we could make SELECT not work, but if you've got COPY then you can
still get all the data, so, yeah, not much different. I seriously doubt
many people are using rules..

Yeah.

We could/should also throw a WARNING if DUMP Is granted to a role

without

BYPASSRLS in case row level security is enabled in the system, I

think.

But

that's more of an implementation detail.

That's a bit ugly and RLS could be added to a relation after the DUMP
privilege is granted.

Yes, it's not going to be all-covering, but it can still be a useful
hint/warning in the cases where it *does* that. We obviously still need
pg_dump to give the error in both scenarios.

I'm not against doing it, personally, but I suspect others won't like it
(or at least, that's been the case in the past with other things..).

Heh, let's defer to a third party then :)

Ok, I see the point you're making that we could make this into a

capability which isn't something which can be expressed through our
existing GRANT system. That strikes me as a solution trying to find a
problem though. There's no need to invent such an oddity for this
particular use-case, I don't think.

Maybe not, but we should make sure we don't paint ourselves into a corner
where we cannot do it in the future either.

Agreed. Do you see a risk here of that?

Not really, anymore, i think :)

For regular permissions, we could just pre-populate the system with

predefined roles and use regular GRANTs to those roles, instead of

relying

on role attributes, which might in that case make it even more clear?

The reason for this approach is to address exactly the nightmare that

is

trying to maintain those regular permissions across all the objects in
the system. Today, people simply give the role trying to do the

pg_dump

superuser, which is the best option we have. Saying 'grant SELECT on
all the tables and USAGE on all the schemas' isn't suggested because
it's a huge pain. This role attribute provides a middle ground where
the pg_dump'ing role isn't a superuser, but you don't have to ensure
usage and select are granted to it for every relation.

No, what I'm saying is we could have *predefined role* that allows

"select

on all the tables and usage on all the schemas". And you are unable to
actually remove that. It's not stored on the object, so you cannot REVOKE
the permission on the *object*. Since it's not store on the object it

will

also automatically apply to all new objects, regardless of what you've

done

with DEFAULT PRIVILEGES etc.

But you can grant users and other roles access to this role, and it's

dealt

with like other roles from the "who has it" perspective, instead of being
special-cased.

Instead of "ALTER USER foo WITH DUMP" (or whatever), you'd just do a

"GRANT

pgdump TO foo" (which would then also work through things like group
membership as well)

There's a definite backwards-compatibility concern with this, of course,
but I see where you're coming from. This only really applies with this
particular pg_dump-related role-attribute discussion though, right?

Yes, I believe so. Because it's so similar to the regular permissions,
where as something like "being able to take a base backup" is more of a
boolean - it doesn't apply to actual objects in the database cluster, it's
more global.

This role wouldn't be able to be logged in with, I presume?

Definitely not.

Would it
show up when you run \du? What about in pg_authid? I feel like it
would have to and I worry that users might not care for it- and what
happens if they want to remove it?

Well, going by experience from other systems that have such a role, I'd say
yes it should show up, and it should throw an error when you try to remove
it.

The question about role-attribute vs. inheirited-right is one that I've

been wondering about also though.

This diverges a bit from the actual role attribute discussion here, but I
think a better solution to this is to actually have a separate interface
rather than pg_start/pg_stop backup. One of the main problems with
pg_start/pg_stop vs pg_basebackup is that you can easily leave the system
in a broken state (for example by forgetting to pg_stop_backup, or by
accidentally pg_stop_backup:ing in the middle of someone elses backup).

I agree. I've had this happen to me a number of times and it really
stinks to have your *next* backup fail because the last one failed
half-way and didn't run pg_stop_backup.

I've seen things so much worse than just that :)

pg_basebackup gets around this by automatically executing
do_pg_stop_backup

if the client disconnects. This also lets us allow parallel base backups.
We could have an equivalent functionality exposed through a SQL function,
or argument to pg_start_backup - it would require the backup software to
keep the connection running as it works and then disconnect when it's

done,

but for anything beyond the most trivial shellscript that's not exactly
hard, and it would make the backups safer.

Agreed, I do like that idea.

If we did that, perhaps we don't even need a separate privilege for
pg_start_backup() as it is today, btu can leave that as superuser?

I don't see how this follows though.. We are not talking about only
pg_basebackup or only about where the user running pg_start/stop
has filesystem-level access to the database.

Hmm, yeah, i guess you're right. While from a security perspective they can
already read all the data, they can't read it safely without that. And the
fact that you *also* need a local account with file system access is a
second layer of security or something like that.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#63Robert Haas
robertmhaas@gmail.com
In reply to: Adam Brightwell (#43)
Re: Additional role attributes && superuser review

On Wed, Dec 24, 2014 at 12:48 PM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

* BACKUP - allows role to perform backup operations
* LOGROTATE - allows role to rotate log files
* MONITOR - allows role to view pg_stat_* details
* PROCSIGNAL - allows role to signal backend processes

How about just "SIGNAL" instead of "PROCSIGNAL"?

Generally, I think we'll be happier if these capabilities have names
that are actual words - or combinations of words - rather than partial
words, so I'd suggest avoiding things like PROC for PROCESS and AUTH
for AUTHORIZATION.

In this particular case, it seems like the name of the capability is
based off the name of an internal system data structure. That's the
sort of thing that we do not want to expose to users. As far as we
can, we should try to describe what the capability allows, not the
details of how that is (currently) implemented.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#64Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Robert Haas (#63)
Re: Additional role attributes && superuser review

On Mon, Jan 5, 2015 at 11:49 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Dec 24, 2014 at 12:48 PM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

* BACKUP - allows role to perform backup operations
* LOGROTATE - allows role to rotate log files
* MONITOR - allows role to view pg_stat_* details
* PROCSIGNAL - allows role to signal backend processes

How about just "SIGNAL" instead of "PROCSIGNAL"?

Sure.

Generally, I think we'll be happier if these capabilities have names
that are actual words - or combinations of words - rather than partial
words, so I'd suggest avoiding things like PROC for PROCESS and AUTH
for AUTHORIZATION.

Agreed.

In this particular case, it seems like the name of the capability is
based off the name of an internal system data structure. That's the
sort of thing that we do not want to expose to users. As far as we
can, we should try to describe what the capability allows, not the
details of how that is (currently) implemented.

Agreed.

If others are also in agreement on this point then I'll update the patch
accordingly.

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#65Stephen Frost
sfrost@snowman.net
In reply to: Adam Brightwell (#64)
Re: Additional role attributes && superuser review

Adam, all,

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

If others are also in agreement on this point then I'll update the patch
accordingly.

Works for me.

Thanks!

Stephen

#66Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Stephen Frost (#65)
1 attachment(s)
Re: Additional role attributes && superuser review

All,

Attached is a patch that proposes the following additional role attributes
for review:

* ONLINE_BACKUP - allows role to perform backup operations
- originally proposed as BACKUP - due to concern for the use of that term
in relation to other potential backup related permissions this form is in
line with the documentation as it describes the affected backup operations
as being 'online backups'.
- applies only to the originally proposed backup functions.
* XLOG_REPLAY - allows role to perform pause and resume on xlog_replay
operations ('pg_xlog_replay_pause' and 'pg_xlog_replay_resume')
- following the recommendation from Stephen and Magnus.
* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations
* MONITOR - allows role to view pg_stat_* details (as originally proposed)
* SIGNAL - allows role to signal backend processes (as originally proposed)

The documentation still needs to be updated. If this these attributes and
the capabilities they provide are acceptable, then I'll begin moving
forward on making those updates as well.

Regarding the discussion on a DUMP/READONLY permission. I believe that
topic needs much further discussion and decided it is probably best to keep
it as a separate patch/effort. I'd certainly be willing to continue that
discussion and assist in moving any related effort forward, therefore,
please let me know if there is anything I can do to help.

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

Attachments:

role-attributes-v2.patchtext/x-patch; charset=US-ASCII; name=role-attributes-v2.patchDownload
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 2179bf7..aaf13c1
*** 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_online_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser, replication role or online 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_online_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser, replication role or online 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_online_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or online 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_online_backup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or online backup role to create a restore point"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
*************** pg_xlogfile_name(PG_FUNCTION_ARGS)
*** 338,347 ****
  Datum
  pg_xlog_replay_pause(PG_FUNCTION_ARGS)
  {
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
--- 341,350 ----
  Datum
  pg_xlog_replay_pause(PG_FUNCTION_ARGS)
  {
! 	if (!has_xlog_replay_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or xlog replay role to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
*************** pg_xlog_replay_pause(PG_FUNCTION_ARGS)
*** 360,369 ****
  Datum
  pg_xlog_replay_resume(PG_FUNCTION_ARGS)
  {
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
--- 363,372 ----
  Datum
  pg_xlog_replay_resume(PG_FUNCTION_ARGS)
  {
! 	if (!has_xlog_replay_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or xlog replay role to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index 1e3888e..6ad8294
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** has_createrole_privilege(Oid roleid)
*** 5080,5085 ****
--- 5080,5110 ----
  	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 ****
--- 5122,5239 ----
  		ReleaseSysCache(utup);
  	}
  	return result;
+ }
+ 
+ /*
+  * Check whether specified role has BACKUP privilege (or is a superuser)
+  */
+ bool
+ has_online_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))->rolonlinebackup;
+ 		ReleaseSysCache(utup);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has XLOGREPLAY privilege (or is a superuser)
+  */
+ bool
+ has_xlog_replay_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))->rolxlogreplay;
+ 		ReleaseSysCache(utup);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has LOG privilege (or is a superuser)
+  */
+ bool
+ has_log_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))->rollog;
+ 		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 SIGNAL privilege (or is a superuser)
+  */
+ bool
+ has_signal_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))->rolsignal;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
  }
  
  /*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 2210eed..e067516
*** 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,89 ----
  	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		onlinebackup = false;
+ 	bool		xlogreplay = false;
+ 	bool		log = false;
+ 	bool		monitor = false;
+ 	bool		signal = 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 ****
--- 104,114 ----
  	DefElem    *dadminmembers = NULL;
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
+ 	DefElem    *donlinebackup = NULL;
+ 	DefElem    *dxlogreplay = NULL;
+ 	DefElem    *dlog = NULL;
+ 	DefElem    *dmonitor = NULL;
+ 	DefElem    *dsignal = NULL;
  
  	/* The defaults can vary depending on the original statement type */
  	switch (stmt->stmt_type)
*************** CreateRole(CreateRoleStmt *stmt)
*** 242,247 ****
--- 243,288 ----
  						 errmsg("conflicting or redundant options")));
  			dbypassRLS = defel;
  		}
+ 		else if (strcmp(defel->defname, "onlinebackup") == 0)
+ 		{
+ 			if (donlinebackup)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			donlinebackup = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "xlogreplay") == 0)
+ 		{
+ 			if (dxlogreplay)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dxlogreplay = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "log") == 0)
+ 		{
+ 			if (dlog)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dlog = 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, "signal") == 0)
+ 		{
+ 			if (dsignal)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dsignal = defel;
+ 		}
  		else
  			elog(ERROR, "option \"%s\" not recognized",
  				 defel->defname);
*************** CreateRole(CreateRoleStmt *stmt)
*** 279,284 ****
--- 320,335 ----
  		validUntil = strVal(dvalidUntil->arg);
  	if (dbypassRLS)
  		bypassrls = intVal(dbypassRLS->arg) != 0;
+ 	if (donlinebackup)
+ 		onlinebackup = intVal(donlinebackup->arg) != 0;
+ 	if (dxlogreplay)
+ 		xlogreplay = intVal(dxlogreplay->arg) != 0;
+ 	if (dlog)
+ 		log = intVal(dlog->arg) != 0;
+ 	if (dmonitor)
+ 		monitor = intVal(dmonitor->arg) != 0;
+ 	if (dsignal)
+ 		signal = intVal(dsignal->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")));
--- 355,361 ----
  	}
  	else
  	{
! 		if (!has_createrole_privilege(GetUserId()))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to create role")));
*************** CreateRole(CreateRoleStmt *stmt)
*** 395,400 ****
--- 446,456 ----
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  
  	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
+ 	new_record[Anum_pg_authid_rolonlinebackup - 1] = BoolGetDatum(onlinebackup);
+ 	new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay);
+ 	new_record[Anum_pg_authid_rollog - 1] = BoolGetDatum(log);
+ 	new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor);
+ 	new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal);
  
  	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
  
*************** AlterRole(AlterRoleStmt *stmt)
*** 496,501 ****
--- 552,562 ----
  	Datum		validUntil_datum;		/* same, as timestamptz Datum */
  	bool		validUntil_null;
  	bool		bypassrls = -1;
+ 	bool		onlinebackup = -1;
+ 	bool		xlogreplay = -1;
+ 	bool		log = -1;
+ 	bool		monitor = -1;
+ 	bool		signal = -1;
  	DefElem    *dpassword = NULL;
  	DefElem    *dissuper = NULL;
  	DefElem    *dinherit = NULL;
*************** AlterRole(AlterRoleStmt *stmt)
*** 507,512 ****
--- 568,578 ----
  	DefElem    *drolemembers = NULL;
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
+ 	DefElem    *donlinebackup = NULL;
+ 	DefElem    *dxlogreplay = NULL;
+ 	DefElem    *dlog = NULL;
+ 	DefElem    *dmonitor = NULL;
+ 	DefElem    *dsignal = NULL;
  	Oid			roleid;
  
  	/* Extract options from the statement node tree */
*************** AlterRole(AlterRoleStmt *stmt)
*** 609,614 ****
--- 675,720 ----
  						 errmsg("conflicting or redundant options")));
  			dbypassRLS = defel;
  		}
+ 		else if (strcmp(defel->defname, "onlinebackup") == 0)
+ 		{
+ 			if (donlinebackup)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			donlinebackup = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "xlogreplay") == 0)
+ 		{
+ 			if (dxlogreplay)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dxlogreplay = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "log") == 0)
+ 		{
+ 			if (dlog)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dlog = 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, "signal") == 0)
+ 		{
+ 			if (dsignal)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dsignal = defel;
+ 		}
  		else
  			elog(ERROR, "option \"%s\" not recognized",
  				 defel->defname);
*************** AlterRole(AlterRoleStmt *stmt)
*** 642,647 ****
--- 748,763 ----
  		validUntil = strVal(dvalidUntil->arg);
  	if (dbypassRLS)
  		bypassrls = intVal(dbypassRLS->arg);
+ 	if (donlinebackup)
+ 		onlinebackup = intVal(donlinebackup->arg);
+ 	if (dxlogreplay)
+ 		xlogreplay = intVal(dxlogreplay->arg);
+ 	if (dlog)
+ 		log = intVal(dlog->arg);
+ 	if (dmonitor)
+ 		monitor = intVal(dmonitor->arg);
+ 	if (dsignal)
+ 		signal = intVal(dsignal->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 &&
--- 798,815 ----
  					(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 &&
+ 			  onlinebackup < 0 &&
+ 			  xlogreplay < 0 &&
+ 			  log < 0 &&
+ 			  monitor < 0 &&
+ 			  signal < 0 &&
  			  !dconnlimit &&
  			  !rolemembers &&
  			  !validUntil &&
*************** AlterRole(AlterRoleStmt *stmt)
*** 821,826 ****
--- 942,977 ----
  		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
  	}
  
+ 	if (onlinebackup >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolonlinebackup - 1] = BoolGetDatum(onlinebackup > 0);
+ 		new_record_repl[Anum_pg_authid_rolonlinebackup - 1] = true;
+ 	}
+ 
+ 	if (xlogreplay >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay > 0);
+ 		new_record_repl[Anum_pg_authid_rolxlogreplay - 1] = true;
+ 	}
+ 
+ 	if (log >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rollog - 1] = BoolGetDatum(log > 0);
+ 		new_record_repl[Anum_pg_authid_rollog - 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 (signal >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal > 0);
+ 		new_record_repl[Anum_pg_authid_rolsignal - 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),
--- 1049,1055 ----
  		}
  		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")));
--- 1102,1108 ----
  				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")));
--- 1333,1339 ----
  	}
  	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),
--- 1560,1566 ----
  	}
  	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),
--- 1706,1712 ----
  	}
  	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 36dac29..ba35d61
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** AlterOptRoleElem:
*** 977,982 ****
--- 977,1002 ----
  						 */
  						$$ = makeDefElem("inherit", (Node *)makeInteger(FALSE));
  					}
+ 					else if (strcmp($1, "online_backup") == 0)
+ 						$$ = makeDefElem("onlinebackup", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "noonline_backup") == 0)
+ 						$$ = makeDefElem("onlinebackup", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "xlog_replay") == 0)
+ 						$$ = makeDefElem("xlogreplay", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "noxlog_replay") == 0)
+ 						$$ = makeDefElem("xlogreplay", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "log") == 0)
+ 						$$ = makeDefElem("log", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nolog") == 0)
+ 						$$ = makeDefElem("log", (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, "signal") == 0)
+ 						$$ = makeDefElem("signal", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nosignal") == 0)
+ 						$$ = makeDefElem("signal", (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 3be5263..b7e7947
*** a/src/backend/replication/logical/logicalfuncs.c
--- b/src/backend/replication/logical/logicalfuncs.c
***************
*** 29,34 ****
--- 29,35 ----
  
  #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
*** 202,216 ****
  	}
  }
  
- 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.
   *
--- 203,208 ----
*************** pg_logical_slot_get_changes_guts(Functio
*** 324,330 ****
  	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	check_permissions();
  
  	CheckLogicalDecodingRequirements();
  
--- 316,325 ----
  	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 f31925d..35e1c11
*** 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 86c36bf..e1e44b0
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
***************
*** 71,76 ****
--- 71,77 ----
  #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
*** 2803,2813 ****
  		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);
  		}
--- 2804,2815 ----
  		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/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644
index 29f7c3b..ca4b64d
*** 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 SIGNAL permission, then permission is denied.
! 	 */
! 	if (!has_privs_of_role(GetUserId(), proc->roleId)
! 		&& !has_signal_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_log_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or have log 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 389ea49..2107490
*** 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 4646e09..4bac170
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
*************** SetUserIdAndContext(Oid userid, bool sec
*** 430,454 ****
  		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
   */
--- 430,435 ----
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
new file mode 100644
index 1f5cf06..df9f0b4
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** InitPostgres(const char *in_dbname, Oid
*** 762,768 ****
  	{
  		Assert(!bootstrap);
  
! 		if (!superuser() && !has_rolreplication(GetUserId()))
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser or replication role to start walsender")));
--- 762,768 ----
  	{
  		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 e01e6aa..6b8e32d
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
*************** CATALOG(pg_authid,1260) BKI_SHARED_RELAT
*** 53,58 ****
--- 53,63 ----
  	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		rolonlinebackup;/* allowed to peform backup operations? */
+ 	bool		rolxlogreplay;	/* allowed to control xlog replay */
+ 	bool		rollog;			/* allowed to rotate log files? */
+ 	bool		rolmonitor;		/* allowed to view pg_stat_* details? */
+ 	bool		rolsignal;		/* 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
--- 79,85 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					17
  #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
--- 89,102 ----
  #define Anum_pg_authid_rolcanlogin		7
  #define Anum_pg_authid_rolreplication	8
  #define Anum_pg_authid_rolbypassrls		9
! #define Anum_pg_authid_rolonlinebackup	10
! #define Anum_pg_authid_rolxlogreplay	11
! #define Anum_pg_authid_rollog			12
! #define Anum_pg_authid_rolmonitor		13
! #define Anum_pg_authid_rolsignal		14
! #define Anum_pg_authid_rolconnlimit		15
! #define Anum_pg_authid_rolpassword		16
! #define Anum_pg_authid_rolvaliduntil	17
  
  /* ----------------
   *		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
  
--- 105,111 ----
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t 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 6e33a17..595564f
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
*************** extern void ValidatePgVersion(const char
*** 442,448 ****
  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);
--- 442,447 ----
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
new file mode 100644
index ab0df6c..39bc285
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** extern bool pg_event_trigger_ownercheck(
*** 328,332 ****
--- 328,338 ----
  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_online_backup_privilege(Oid roleid);
+ extern bool has_xlog_replay_privilege(Oid roleid);
+ extern bool has_log_privilege(Oid roleid);
+ extern bool has_monitor_privilege(Oid roleid);
+ extern bool has_signal_privilege(Oid roleid);
  
  #endif   /* ACL_H */
#67Robert Haas
robertmhaas@gmail.com
In reply to: Adam Brightwell (#66)
Re: Additional role attributes && superuser review

On Thu, Jan 15, 2015 at 6:03 PM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

* ONLINE_BACKUP - allows role to perform backup operations
- originally proposed as BACKUP - due to concern for the use of that term
in relation to other potential backup related permissions this form is in
line with the documentation as it describes the affected backup operations
as being 'online backups'.
- applies only to the originally proposed backup functions.

I'm slightly mystified as to how including the word "online" helps
here. It's unlikely that there will be an offline_backup permission,
because if the system is off-line, SQL-level permissions are
irrelevant.

* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations

Maybe LOGFILE? Only because some confusion with the LOG message level
seems possible; or confusion about whether this is a permission that
lets you log things.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Robert Haas (#67)
Re: Additional role attributes && superuser review

Robert,

Thanks for the feedback.

I'm slightly mystified as to how including the word "online" helps

here. It's unlikely that there will be an offline_backup permission,
because if the system is off-line, SQL-level permissions are
irrelevant.

I'm certainly open to recommendations on this one. Initially, BACKUP was
proposed, but based on the discussion, it is unacceptable. As mentioned,
the documentation for the affected functions refer to starting/stopping an
'on-line backup', hence the current proposal. I feel like it is obviously
more in line with the documentation and removes the ambiguity in what
'type' of backup it allows, as that seemed to be one of the major concerns
of just using BACKUP. However, I could certainly understand if there was a
confusion on the terminology of 'online' vs 'offline' if those are not
regularly used terms or concepts. At any rate, I'll certainly continue to
give this one thought, but I wouldn't mind any recommendations/suggestions
anyone was willing to throw my way.

* LOG - allows role to rotate log files - remains broad enough to consider

future log related operations

Maybe LOGFILE? Only because some confusion with the LOG message level
seems possible; or confusion about whether this is a permission that
lets you log things.

That's a good point. I'll change this one up.

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#69Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#67)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 15, 2015 at 6:03 PM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

* ONLINE_BACKUP - allows role to perform backup operations
- originally proposed as BACKUP - due to concern for the use of that term
in relation to other potential backup related permissions this form is in
line with the documentation as it describes the affected backup operations
as being 'online backups'.
- applies only to the originally proposed backup functions.

I'm slightly mystified as to how including the word "online" helps
here. It's unlikely that there will be an offline_backup permission,
because if the system is off-line, SQL-level permissions are
irrelevant.

ONLINE does match up with what we call the pg_start/stop_backup based
backups in the documentation, at least. Also, it's intended to contrast
against pg_dump-based backups, not offline backups (which we don't
discuss at all in the docs that I can see).

Looking over the docs again a bit though, what about BACKUP_CONTROL,
following the title of 9.26.3?

Suggestions certainly welcome.

* LOG - allows role to rotate log files - remains broad enough to consider
future log related operations

Maybe LOGFILE? Only because some confusion with the LOG message level
seems possible; or confusion about whether this is a permission that
lets you log things.

LOGFILE works for me.

Thanks!

Stephen

#70Simon Riggs
simon@2ndQuadrant.com
In reply to: Stephen Frost (#34)
Re: Additional role attributes && superuser review

On 3 November 2014 at 17:08, Stephen Frost <sfrost@snowman.net> wrote:

role attributes don't act like
GRANTs anyway (there's no ADMIN option and they aren't inheirited).

I'm happy with us *not* doing this using GRANTs, as long as we spend
some love on the docs to show there is a very clear distinction
between the two.

Users get confused between privs, role attributes and SETs that apply to roles.

Introducing the new word "capability" needs to also have some clarity.
Is that the same thing as "role attribute", or is that a 4th kind of
thang?

Make things make sense and they're easy to agree.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, RemoteDBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#71Stephen Frost
sfrost@snowman.net
In reply to: Simon Riggs (#70)
Re: Additional role attributes && superuser review

* Simon Riggs (simon@2ndQuadrant.com) wrote:

On 3 November 2014 at 17:08, Stephen Frost <sfrost@snowman.net> wrote:

role attributes don't act like
GRANTs anyway (there's no ADMIN option and they aren't inheirited).

I'm happy with us *not* doing this using GRANTs, as long as we spend
some love on the docs to show there is a very clear distinction
between the two.

The distinction already exists. I agree that the documentation should
be improved to clarify how GRANT'd privileges are different from role
attributes (which is what our existing superuser, createrole, etc
options are).

Users get confused between privs, role attributes and SETs that apply to roles.

Agreed.

Introducing the new word "capability" needs to also have some clarity.
Is that the same thing as "role attribute", or is that a 4th kind of
thang?

At present, it's exactly the same as 'role attribute' and, for my part
at least, I was thinking it would remain the same. I believe the idea
was to migrate the terminology from 'role attribute' to 'capability' as
the latter better represents both the existing options and the new ones.

Thanks!

Stephen

#72Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Robert Haas (#67)
Re: Additional role attributes && superuser review

All,

I'm slightly mystified as to how including the word "online" helps
here. It's unlikely that there will be an offline_backup permission,
because if the system is off-line, SQL-level permissions are
irrelevant.

After re-reading through this thread is seems like EXCLUSIVEBACKUP
(proposed by Magnus) seemed to be a potentially acceptable alternative.

----
Relevant post:
/messages/by-id/CABUevEz7BZ0r85VUt4RVXX0JkpiH8hP8ToqzGVpuFL0KvcvBNg@mail.gmail.com
----
We need to separate the logical backups (pg_dump) from the physical ones
(start/stop+filesystem and pg_basebackup). We might also need to separate
the two different ways of doing physical backups.

Personalyl I think using the DUMP name makes that a lot more clear. Maybe
we need to avoid using BACKUP alone as well, to make sure it doesn't go the
other way - using BASEBACKUP and EXCLUSIVEBACKUP for those two different
ones perhaps?
----

----
Relevant post:
/messages/by-id/20141231144610.GS3062@tamriel.snowman.net
----
I think I'm coming around to the EXCLUSIVEBACKUP option, per the
discussion with Magnus. I don't particularly like LOGBACKUP and don't
think it really makes sense, while PHYSBACKUP seems like it'd cover what
REPLICATION already does.
----

Thoughts?

-Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#73Stephen Frost
sfrost@snowman.net
In reply to: Adam Brightwell (#72)
Re: Additional role attributes && superuser review

Adam, all,

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

After re-reading through this thread is seems like EXCLUSIVEBACKUP
(proposed by Magnus) seemed to be a potentially acceptable alternative.

I just chatted a bit on IRC w/ Magnus about this and I'm on-board with
his proposal to use EXCLUSIVEBACKUP, but there's a couple additional
things which need doing as part of that:

We need to actually define what an 'exclusive backup' is in the
documentation- it's not there today.

pg_start_backup/pg_stop_backup docs should be updated to refer to those
operations as relating to "on-line exclusive backup," same as
pg_is_in_backup() and pg_backup_start_time() do.

The docs for the EXCLUSIVEBACKUP attribute should, of course, refer to
where EXCLUSIVE BACKUP is defined.

Hopefully that wraps up the last of the debate around the naming of
these options and we can move forward, once the patch is updated to
reflect this and docs are written. Would be great to hear from anyone
who feels otherwise.

Regarding the pg_dump/read-only/etc discussion- that wasn't intended to
be part of this and I continue to feel it'd be best addressed on a new
thread specifically for that. Once we have this initial round of role
attribute additions settled, I'd be more than happy to support that
discussion and see what we can do to provide such a role.

Thanks!

Stephen

#74Robert Haas
robertmhaas@gmail.com
In reply to: Adam Brightwell (#72)
Re: Additional role attributes && superuser review

On Wed, Jan 21, 2015 at 11:27 AM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

After re-reading through this thread is seems like EXCLUSIVEBACKUP (proposed
by Magnus) seemed to be a potentially acceptable alternative.

So this would let you do pg_start_backup() and pg_stop_backup(), but
it wouldn't let you run pg_basebackup against the server?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#75Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#74)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Wed, Jan 21, 2015 at 11:27 AM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

After re-reading through this thread is seems like EXCLUSIVEBACKUP (proposed
by Magnus) seemed to be a potentially acceptable alternative.

So this would let you do pg_start_backup() and pg_stop_backup(), but
it wouldn't let you run pg_basebackup against the server?

Right. We already have a role attribute which allows pg_basebackup
(replication). Also, with pg_basebackup / rolreplication, your role
is able to read the entire data directory from the server, that's not
the case with only rights to run pg_start/stop_backup.

In conjunction with enterprise backup solutions and SANs, which offer
similar controls where a generally unprivileged user can have a snapshot
of the system taken through the SAN interface, you can give users the
ability to run ad-hoc backups of the cluster without giving them
superuser-level access or replication-level access.

Even with simpler solutions, it means that the backup user doesn't
have to be able to run some superuser-level script against the database
to run the backup.

As for pg_basebackup itself, I agree that it's not exactly intuitive
that 'replication' is what grants you the right to run pg_basebackup..
Perhaps we could rename it or make an alias for it, or something along
those lines? I wasn't looking to do anything with it at this time, but
it would probably be good to improve it somehow, if you (or anyone) have
suggestions on how best to do so.

Thanks!

Stephen

#76Andres Freund
andres@2ndquadrant.com
In reply to: Stephen Frost (#75)
Re: Additional role attributes && superuser review

On 2015-01-26 13:47:02 -0500, Stephen Frost wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Wed, Jan 21, 2015 at 11:27 AM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

After re-reading through this thread is seems like EXCLUSIVEBACKUP (proposed
by Magnus) seemed to be a potentially acceptable alternative.

So this would let you do pg_start_backup() and pg_stop_backup(), but
it wouldn't let you run pg_basebackup against the server?

Right. We already have a role attribute which allows pg_basebackup
(replication). Also, with pg_basebackup / rolreplication, your role
is able to read the entire data directory from the server, that's not
the case with only rights to run pg_start/stop_backup.

In conjunction with enterprise backup solutions and SANs, which offer
similar controls where a generally unprivileged user can have a snapshot
of the system taken through the SAN interface, you can give users the
ability to run ad-hoc backups of the cluster without giving them
superuser-level access or replication-level access.

I'm sorry if this has already been discussed, but the thread is awfully
long already. But what's actually the point of having a separate
EXCLUSIVEBACKUP permission? Using it still requires full file system
access to the data directory, so the additional permissions granted by
replication aren't really relevant.

I don't think the comparison with the SAN snapshot functionality is apt:
The SAN solution itself will still run with full data access. Just
pressing the button for the snapshot requires less. You're comparing
that button to pg_start/stop_backup() - but that doesn't make sense,
because it's only useful if somebody actually takes the backup during
that time.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#77Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#76)
Re: Additional role attributes && superuser review

On Mon, Jan 26, 2015 at 1:59 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2015-01-26 13:47:02 -0500, Stephen Frost wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Wed, Jan 21, 2015 at 11:27 AM, Adam Brightwell
<adam.brightwell@crunchydatasolutions.com> wrote:

After re-reading through this thread is seems like EXCLUSIVEBACKUP (proposed
by Magnus) seemed to be a potentially acceptable alternative.

So this would let you do pg_start_backup() and pg_stop_backup(), but
it wouldn't let you run pg_basebackup against the server?

Right. We already have a role attribute which allows pg_basebackup
(replication). Also, with pg_basebackup / rolreplication, your role
is able to read the entire data directory from the server, that's not
the case with only rights to run pg_start/stop_backup.

In conjunction with enterprise backup solutions and SANs, which offer
similar controls where a generally unprivileged user can have a snapshot
of the system taken through the SAN interface, you can give users the
ability to run ad-hoc backups of the cluster without giving them
superuser-level access or replication-level access.

I'm sorry if this has already been discussed, but the thread is awfully
long already. But what's actually the point of having a separate
EXCLUSIVEBACKUP permission? Using it still requires full file system
access to the data directory, so the additional permissions granted by
replication aren't really relevant.

That's not necessarily true. You could be able to run a command like
"san_snapshot $PGDATA" without necessarily having the permissions to
inspect the contents of the resulting snapshot. Of course somebody
should be doing that, but in accord with the principle of least
privilege, there's no reason that the account running the unattended
backup needs to have those rights.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#78Stephen Frost
sfrost@snowman.net
In reply to: Andres Freund (#76)
Re: Additional role attributes && superuser review

* Andres Freund (andres@2ndquadrant.com) wrote:

On 2015-01-26 13:47:02 -0500, Stephen Frost wrote:

Right. We already have a role attribute which allows pg_basebackup
(replication). Also, with pg_basebackup / rolreplication, your role
is able to read the entire data directory from the server, that's not
the case with only rights to run pg_start/stop_backup.

In conjunction with enterprise backup solutions and SANs, which offer
similar controls where a generally unprivileged user can have a snapshot
of the system taken through the SAN interface, you can give users the
ability to run ad-hoc backups of the cluster without giving them
superuser-level access or replication-level access.

I'm sorry if this has already been discussed, but the thread is awfully
long already. But what's actually the point of having a separate
EXCLUSIVEBACKUP permission? Using it still requires full file system
access to the data directory, so the additional permissions granted by
replication aren't really relevant.

I agree that it's a pretty long thread for what amount to a few
relatively straight-forward role attributes (at least, in my view).

I don't think the comparison with the SAN snapshot functionality is apt:
The SAN solution itself will still run with full data access. Just
pressing the button for the snapshot requires less. You're comparing
that button to pg_start/stop_backup() - but that doesn't make sense,
because it's only useful if somebody actually takes the backup during
that time.

I'm not following your logic here.. You're right- just pressing the
button to take a snapshot can be granted out to a lower-level user using
the SAN solution. That snapshot's useless unless the user can first run
pg_start_backup though (and subsequently run pg_stop_backup afterwards).
Clearly, XLOG archiving has to be set up already, but that would be set
up when the system is initially brought online.

This capability would be used in conjunction with the SAN snapshot
capability, it's not intended to be a comparison to what SANs offer.

Thanks!

Stephen

#79Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#77)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 26, 2015 at 1:59 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2015-01-26 13:47:02 -0500, Stephen Frost wrote:

Right. We already have a role attribute which allows pg_basebackup
(replication). Also, with pg_basebackup / rolreplication, your role
is able to read the entire data directory from the server, that's not
the case with only rights to run pg_start/stop_backup.

In conjunction with enterprise backup solutions and SANs, which offer
similar controls where a generally unprivileged user can have a snapshot
of the system taken through the SAN interface, you can give users the
ability to run ad-hoc backups of the cluster without giving them
superuser-level access or replication-level access.

I'm sorry if this has already been discussed, but the thread is awfully
long already. But what's actually the point of having a separate
EXCLUSIVEBACKUP permission? Using it still requires full file system
access to the data directory, so the additional permissions granted by
replication aren't really relevant.

That's not necessarily true. You could be able to run a command like
"san_snapshot $PGDATA" without necessarily having the permissions to
inspect the contents of the resulting snapshot. Of course somebody
should be doing that, but in accord with the principle of least
privilege, there's no reason that the account running the unattended
backup needs to have those rights.

Right! You explained it more clearly than I did.

Thanks!

Stephen

#80Andres Freund
andres@2ndquadrant.com
In reply to: Stephen Frost (#78)
Re: Additional role attributes && superuser review

On 2015-01-26 14:05:03 -0500, Stephen Frost wrote:

This capability would be used in conjunction with the SAN snapshot
capability, it's not intended to be a comparison to what SANs offer.

Oh, on a reread that's now clear. Many of those actually allow hooks to
be run when taking a snapshot, that'd probably be a better approach. But
I can now see the point.

Thanks,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#81Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Stephen Frost (#73)
1 attachment(s)
Re: Additional role attributes && superuser review

All,

I have attached and updated patch for review.

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

Attachments:

role-attributes-v3.patchtext/x-patch; charset=US-ASCII; name=role-attributes-v3.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
new file mode 100644
index 62305d2..fd4b9ab
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1445,1450 ****
--- 1445,1486 ----
       </row>
  
       <row>
+       <entry><structfield>rolbypassrls</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry>Role can bypass row level security</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>rolexclbackup</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry>Role can perform on-line exclusive backup operations</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>rolxlogreplay</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry>Role can control xlog recovery replay operations</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>rollogfile</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry>Role can rotate log files</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>rolmonitor</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry>Role can view pg_stat_* details</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>rolsignal</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry>Role can signal backend processes</entry>
+      </row>
+ 
+      <row>
        <entry><structfield>rolconnlimit</structfield></entry>
        <entry><type>int4</type></entry>
        <entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index d57243a..096c770
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT set_config('log_statement_stats',
*** 16425,16438 ****
          <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
          </entry>
         <entry><type>pg_lsn</type></entry>
!        <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_stop_backup()</function></literal>
          </entry>
         <entry><type>pg_lsn</type></entry>
!        <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
         <entry>
--- 16425,16438 ----
          <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
          </entry>
         <entry><type>pg_lsn</type></entry>
!        <entry>Prepare for performing on-line exclusive backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
         <entry>
          <literal><function>pg_stop_backup()</function></literal>
          </entry>
         <entry><type>pg_lsn</type></entry>
!        <entry>Finish performing on-line exclusive backup (restricted to superusers or replication roles)</entry>
        </row>
        <row>
         <entry>
diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
new file mode 100644
index ea26027..0fc3b42
*** a/doc/src/sgml/ref/create_role.sgml
--- b/doc/src/sgml/ref/create_role.sgml
*************** CREATE ROLE <replaceable class="PARAMETE
*** 33,38 ****
--- 33,43 ----
      | LOGIN | NOLOGIN
      | REPLICATION | NOREPLICATION
      | BYPASSRLS | NOBYPASSRLS
+     | EXCLUSIVEBACKUP | NOEXCLUSIVEBACKUP
+     | XLOGREPLAY | NOXLOGREPLAY
+     | LOGFILE | NOLOGFILE
+     | MONITOR | NOMONITOR
+     | SIGNAL | NOSIGNAL
      | CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
      | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
      | VALID UNTIL '<replaceable class="PARAMETER">timestamp</replaceable>'
*************** CREATE ROLE <replaceable class="PARAMETE
*** 209,214 ****
--- 214,283 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry>
+       <term><literal>EXCLUSIVEBACKUP</literal></term>
+       <term><literal>NOEXCLUSIVEBACKUP</literal></term>
+       <listitem>
+        <para>
+        These clauses determine whether a role is allowed to start/stop an
+        on-line exclusive backup.  An on-line exclusive backup is one that is
+        initiated by the low-level base backup api instead of through the
+        replication protocol, see <xref linkend=backup-lowlevel-base-backup>.
+        If not specified, <literal>NOEXCLUSIVEBACKUP</literal> is the default.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
+       <term><literal>XLOGREPLAY</literal></term>
+       <term><literal>NOXLOGREPLAY</literal></term>
+       <listitem>
+        <para>
+        These clauses determine whether a role is allowed to pause/resume xlog
+        recovery.
+        If not specified, <literal>NOXLOGREPLAY</literal> is the default.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
+       <term><literal>LOGFILE</literal></term>
+       <term><literal>NOLOGFILE</literal></term>
+       <listitem>
+        <para>
+        These clauses determine whether a role is allowed to rotate log files.
+        If not specified, <literal>NOLOGFILE</literal> is the default.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
+       <term><literal>MONITOR</literal></term>
+       <term><literal>NOMONITOR</literal></term>
+       <listitem>
+        <para>
+        These clauses determine whether a role is allowed to view details from
+        calling pg_stat_* functions.
+        If not specified, <literal>NOMONITOR</literal> is the default.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
+       <term><literal>SIGNAL</literal></term>
+       <term><literal>NOSIGNAL</literal></term>
+       <listitem>
+        <para>
+        These clauses determine whether a role is allowed to signal backend
+        processes.  A role having the <literal>SIGNAL</literal> attribute will be
+        allowed to signal backend processes that it owns and those roles which it
+        is a member.  Roles with this attribute cannot signal backend processes
+        that are owned by a superuser.
+        If not specified, <literal>NOSIGNAL</literal> is the default.
+        </para>
+       </listitem>
+      </varlistentry>
  
       <varlistentry>
        <term><literal>CONNECTION LIMIT</literal> <replaceable class="parameter">connlimit</replaceable></term>
diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml
new file mode 100644
index 065999c..f7f10c7
*** a/doc/src/sgml/ref/create_user.sgml
--- b/doc/src/sgml/ref/create_user.sgml
*************** CREATE USER <replaceable class="PARAMETE
*** 32,37 ****
--- 32,43 ----
      | INHERIT | NOINHERIT
      | LOGIN | NOLOGIN
      | REPLICATION | NOREPLICATION
+     | BYPASSRLS | NOBYPASSRLS
+     | EXCLUSIVEBACKUP | NOEXCLUSIVEBACKUP
+     | XLOGREPLAY | NOXLOGREPLAY
+     | LOGFILE | NOLOGFILE
+     | MONITOR | NOMONITOR
+     | SIGNAL | NOSIGNAL
      | CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
      | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
      | VALID UNTIL '<replaceable class="PARAMETER">timestamp</replaceable>'
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 2179bf7..12b8a17
*** 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_exclbackup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser, replication role or exclusive 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_exclbackup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser, replication role or exclusive 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_exclbackup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or exclusive 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_exclbackup_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or exclusive backup role to create a restore point"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
*************** pg_xlogfile_name(PG_FUNCTION_ARGS)
*** 338,347 ****
  Datum
  pg_xlog_replay_pause(PG_FUNCTION_ARGS)
  {
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
--- 341,350 ----
  Datum
  pg_xlog_replay_pause(PG_FUNCTION_ARGS)
  {
! 	if (!has_xlog_replay_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or xlog replay role to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
*************** pg_xlog_replay_pause(PG_FUNCTION_ARGS)
*** 360,369 ****
  Datum
  pg_xlog_replay_resume(PG_FUNCTION_ARGS)
  {
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
--- 363,372 ----
  Datum
  pg_xlog_replay_resume(PG_FUNCTION_ARGS)
  {
! 	if (!has_xlog_replay_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or xlog replay role to control recovery"))));
  
  	if (!RecoveryInProgress())
  		ereport(ERROR,
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index 1e3888e..9ed5158
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** has_createrole_privilege(Oid roleid)
*** 5080,5085 ****
--- 5080,5110 ----
  	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 ****
--- 5122,5239 ----
  		ReleaseSysCache(utup);
  	}
  	return result;
+ }
+ 
+ /*
+  * Check whether specified role has BACKUP privilege (or is a superuser)
+  */
+ bool
+ has_exclbackup_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))->rolexclbackup;
+ 		ReleaseSysCache(utup);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has XLOGREPLAY privilege (or is a superuser)
+  */
+ bool
+ has_xlog_replay_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))->rolxlogreplay;
+ 		ReleaseSysCache(utup);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Check whether specified role has LOGFILE privilege (or is a superuser)
+  */
+ bool
+ has_logfile_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))->rollogfile;
+ 		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 SIGNAL privilege (or is a superuser)
+  */
+ bool
+ has_signal_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))->rolsignal;
+ 		ReleaseSysCache(utup);
+ 	}
+ 	return result;
  }
  
  /*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 2210eed..e0eb5c3
*** 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,89 ----
  	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		exclbackup = false;
+ 	bool		xlogreplay = false;
+ 	bool		logfile = false;
+ 	bool		monitor = false;
+ 	bool		signal = 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 ****
--- 104,114 ----
  	DefElem    *dadminmembers = NULL;
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
+ 	DefElem    *dexclbackup = NULL;
+ 	DefElem    *dxlogreplay = NULL;
+ 	DefElem    *dlogfile = NULL;
+ 	DefElem    *dmonitor = NULL;
+ 	DefElem    *dsignal = NULL;
  
  	/* The defaults can vary depending on the original statement type */
  	switch (stmt->stmt_type)
*************** CreateRole(CreateRoleStmt *stmt)
*** 242,247 ****
--- 243,288 ----
  						 errmsg("conflicting or redundant options")));
  			dbypassRLS = defel;
  		}
+ 		else if (strcmp(defel->defname, "exclbackup") == 0)
+ 		{
+ 			if (dexclbackup)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dexclbackup = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "xlogreplay") == 0)
+ 		{
+ 			if (dxlogreplay)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dxlogreplay = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "logfile") == 0)
+ 		{
+ 			if (dlogfile)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dlogfile = 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, "signal") == 0)
+ 		{
+ 			if (dsignal)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dsignal = defel;
+ 		}
  		else
  			elog(ERROR, "option \"%s\" not recognized",
  				 defel->defname);
*************** CreateRole(CreateRoleStmt *stmt)
*** 279,284 ****
--- 320,335 ----
  		validUntil = strVal(dvalidUntil->arg);
  	if (dbypassRLS)
  		bypassrls = intVal(dbypassRLS->arg) != 0;
+ 	if (dexclbackup)
+ 		exclbackup = intVal(dexclbackup->arg) != 0;
+ 	if (dxlogreplay)
+ 		xlogreplay = intVal(dxlogreplay->arg) != 0;
+ 	if (dlogfile)
+ 		logfile = intVal(dlogfile->arg) != 0;
+ 	if (dmonitor)
+ 		monitor = intVal(dmonitor->arg) != 0;
+ 	if (dsignal)
+ 		signal = intVal(dsignal->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")));
--- 355,361 ----
  	}
  	else
  	{
! 		if (!has_createrole_privilege(GetUserId()))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to create role")));
*************** CreateRole(CreateRoleStmt *stmt)
*** 395,400 ****
--- 446,456 ----
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  
  	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
+ 	new_record[Anum_pg_authid_rolexclbackup - 1] = BoolGetDatum(exclbackup);
+ 	new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay);
+ 	new_record[Anum_pg_authid_rollogfile - 1] = BoolGetDatum(logfile);
+ 	new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor);
+ 	new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal);
  
  	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
  
*************** AlterRole(AlterRoleStmt *stmt)
*** 496,501 ****
--- 552,562 ----
  	Datum		validUntil_datum;		/* same, as timestamptz Datum */
  	bool		validUntil_null;
  	bool		bypassrls = -1;
+ 	bool		exclbackup = -1;
+ 	bool		xlogreplay = -1;
+ 	bool		logfile = -1;
+ 	bool		monitor = -1;
+ 	bool		signal = -1;
  	DefElem    *dpassword = NULL;
  	DefElem    *dissuper = NULL;
  	DefElem    *dinherit = NULL;
*************** AlterRole(AlterRoleStmt *stmt)
*** 507,512 ****
--- 568,578 ----
  	DefElem    *drolemembers = NULL;
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
+ 	DefElem    *dexclbackup = NULL;
+ 	DefElem    *dxlogreplay = NULL;
+ 	DefElem    *dlogfile = NULL;
+ 	DefElem    *dmonitor = NULL;
+ 	DefElem    *dsignal = NULL;
  	Oid			roleid;
  
  	/* Extract options from the statement node tree */
*************** AlterRole(AlterRoleStmt *stmt)
*** 609,614 ****
--- 675,720 ----
  						 errmsg("conflicting or redundant options")));
  			dbypassRLS = defel;
  		}
+ 		else if (strcmp(defel->defname, "exclbackup") == 0)
+ 		{
+ 			if (dexclbackup)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dexclbackup = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "xlogreplay") == 0)
+ 		{
+ 			if (dxlogreplay)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dxlogreplay = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "logfile") == 0)
+ 		{
+ 			if (dlogfile)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dlogfile = 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, "signal") == 0)
+ 		{
+ 			if (dsignal)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dsignal = defel;
+ 		}
  		else
  			elog(ERROR, "option \"%s\" not recognized",
  				 defel->defname);
*************** AlterRole(AlterRoleStmt *stmt)
*** 642,647 ****
--- 748,763 ----
  		validUntil = strVal(dvalidUntil->arg);
  	if (dbypassRLS)
  		bypassrls = intVal(dbypassRLS->arg);
+ 	if (dexclbackup)
+ 		exclbackup = intVal(dexclbackup->arg);
+ 	if (dxlogreplay)
+ 		xlogreplay = intVal(dxlogreplay->arg);
+ 	if (dlogfile)
+ 		logfile = intVal(dlogfile->arg);
+ 	if (dmonitor)
+ 		monitor = intVal(dmonitor->arg);
+ 	if (dsignal)
+ 		signal = intVal(dsignal->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 &&
--- 798,815 ----
  					(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 &&
+ 			  exclbackup < 0 &&
+ 			  xlogreplay < 0 &&
+ 			  logfile < 0 &&
+ 			  monitor < 0 &&
+ 			  signal < 0 &&
  			  !dconnlimit &&
  			  !rolemembers &&
  			  !validUntil &&
*************** AlterRole(AlterRoleStmt *stmt)
*** 821,826 ****
--- 942,977 ----
  		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
  	}
  
+ 	if (exclbackup >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolexclbackup - 1] = BoolGetDatum(exclbackup > 0);
+ 		new_record_repl[Anum_pg_authid_rolexclbackup - 1] = true;
+ 	}
+ 
+ 	if (xlogreplay >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay > 0);
+ 		new_record_repl[Anum_pg_authid_rolxlogreplay - 1] = true;
+ 	}
+ 
+ 	if (logfile >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rollogfile - 1] = BoolGetDatum(logfile > 0);
+ 		new_record_repl[Anum_pg_authid_rollogfile - 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 (signal >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal > 0);
+ 		new_record_repl[Anum_pg_authid_rolsignal - 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),
--- 1049,1055 ----
  		}
  		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")));
--- 1102,1108 ----
  				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")));
--- 1333,1339 ----
  	}
  	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),
--- 1560,1566 ----
  	}
  	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),
--- 1706,1712 ----
  	}
  	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 36dac29..5374437
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** AlterOptRoleElem:
*** 977,982 ****
--- 977,1002 ----
  						 */
  						$$ = makeDefElem("inherit", (Node *)makeInteger(FALSE));
  					}
+ 					else if (strcmp($1, "exclusivebackup") == 0)
+ 						$$ = makeDefElem("exclbackup", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "noexclusivebackup") == 0)
+ 						$$ = makeDefElem("exclbackup", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "xlogreplay") == 0)
+ 						$$ = makeDefElem("xlogreplay", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "noxlogreplay") == 0)
+ 						$$ = makeDefElem("xlogreplay", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "logfile") == 0)
+ 						$$ = makeDefElem("logfile", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nologfile") == 0)
+ 						$$ = makeDefElem("logfile", (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, "signal") == 0)
+ 						$$ = makeDefElem("signal", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nosignal") == 0)
+ 						$$ = makeDefElem("signal", (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 3be5263..b7e7947
*** a/src/backend/replication/logical/logicalfuncs.c
--- b/src/backend/replication/logical/logicalfuncs.c
***************
*** 29,34 ****
--- 29,35 ----
  
  #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
*** 202,216 ****
  	}
  }
  
- 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.
   *
--- 203,208 ----
*************** pg_logical_slot_get_changes_guts(Functio
*** 324,330 ****
  	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
! 	check_permissions();
  
  	CheckLogicalDecodingRequirements();
  
--- 316,325 ----
  	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 f31925d..35e1c11
*** 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 05d2339..1ace6c0
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
***************
*** 71,76 ****
--- 71,77 ----
  #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
*** 2805,2815 ****
  		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);
  		}
--- 2806,2817 ----
  		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/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644
index 29f7c3b..0c1c31a
*** 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 SIGNAL permission, then permission is denied.
! 	 */
! 	if (!has_privs_of_role(GetUserId(), proc->roleId)
! 		&& !has_signal_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_logfile_privilege(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or have logfile 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 389ea49..2107490
*** 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 4646e09..4bac170
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
*************** SetUserIdAndContext(Oid userid, bool sec
*** 430,454 ****
  		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
   */
--- 430,435 ----
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
new file mode 100644
index 1f5cf06..df9f0b4
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** InitPostgres(const char *in_dbname, Oid
*** 762,768 ****
  	{
  		Assert(!bootstrap);
  
! 		if (!superuser() && !has_rolreplication(GetUserId()))
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser or replication role to start walsender")));
--- 762,768 ----
  	{
  		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 e01e6aa..dd1ed37
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
*************** CATALOG(pg_authid,1260) BKI_SHARED_RELAT
*** 53,58 ****
--- 53,63 ----
  	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		rolexclbackup;	/* allowed to peform backup operations? */
+ 	bool		rolxlogreplay;	/* allowed to control xlog replay */
+ 	bool		rollogfile;		/* allowed to rotate log files? */
+ 	bool		rolmonitor;		/* allowed to view pg_stat_* details? */
+ 	bool		rolsignal;		/* 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
--- 79,85 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					17
  #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
--- 89,102 ----
  #define Anum_pg_authid_rolcanlogin		7
  #define Anum_pg_authid_rolreplication	8
  #define Anum_pg_authid_rolbypassrls		9
! #define Anum_pg_authid_rolexclbackup	10
! #define Anum_pg_authid_rolxlogreplay	11
! #define Anum_pg_authid_rollogfile		12
! #define Anum_pg_authid_rolmonitor		13
! #define Anum_pg_authid_rolsignal		14
! #define Anum_pg_authid_rolconnlimit		15
! #define Anum_pg_authid_rolpassword		16
! #define Anum_pg_authid_rolvaliduntil	17
  
  /* ----------------
   *		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
  
--- 105,111 ----
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t 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 6e33a17..595564f
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
*************** extern void ValidatePgVersion(const char
*** 442,448 ****
  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);
--- 442,447 ----
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
new file mode 100644
index ab0df6c..63c702b
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** extern bool pg_event_trigger_ownercheck(
*** 328,332 ****
--- 328,338 ----
  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_exclbackup_privilege(Oid roleid);
+ extern bool has_xlog_replay_privilege(Oid roleid);
+ extern bool has_logfile_privilege(Oid roleid);
+ extern bool has_monitor_privilege(Oid roleid);
+ extern bool has_signal_privilege(Oid roleid);
  
  #endif   /* ACL_H */
#82Stephen Frost
sfrost@snowman.net
In reply to: Adam Brightwell (#81)
1 attachment(s)
Re: Additional role attributes && superuser review

Adam,

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

I have attached and updated patch for review.

Thanks! I've gone over this and made quite a few documentation and
comment updates, but not too much else, so I'm pretty happy with how
this is coming along. As mentioned elsewhere, this conflicts with the
GetUserId() to has_privs_of_role() cleanup, but as I anticipate handling
both this patch and that one, I'll find some way to manage. :)

Updated patch attached. Barring objections, I'll be moving forward with
this soonish. Would certainly appreciate any additional testing or
review that you (or anyone!) has time to provide.

Thanks again!

Stephen

Attachments:

role-attributes-v4.patchtext/x-diff; charset=us-asciiDownload
From 58efbb5ebeadca15fb2f3ada4ca5c83b7ddd5d69 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Sat, 28 Feb 2015 20:23:38 -0500
Subject: [PATCH] Add role attributes for various operations

Historically, many operations have been restricted to the superuser.
These additional role attributes allow an administrator to delegate the
right to run some of these operations out to other roles, reducing the
number of cases which strictly require superuser rights and therefore,
hopefully, reducing the number of users running as superuser in the
wild.

This patch introduces the following role attributes:

EXCLUSIVEBACKUP - allows the role to run pg_start_backup,
pg_stop_backup, pg_create_restore_point, and pg_switch_xlog.

XLOGREPLAY - allows the role to run pg_xlog_replay_pause and
pg_xlog_replay_resume.

LOGFILE - allows the role to run pg_rotate_logfile

MONITOR - allows the role to see the details of all running processes
(including both normal backends and replication backends).  The
documentation and code are also updated to use has_privs_of_role().

SIGNAL - allows the role to signal all other normal backends (except
those initiated by a superuser) with pg_cancel_backend and
pg_termiante_backend.  The documentation and code for the NOSIGNAL case
are also updated to use has_privs_of_role().

In passing, this also centralizes and standardizes the REPLICATION and
CREATEROLE support functions.

Lots of discussion, review, and commentary from Jim Nasby, Simon,
Magnus, Alvaro, and Robert.

Authored by Adam, bugs and various documentation updates from
me.
---
 doc/src/sgml/catalogs.sgml                     |  30 ++++
 doc/src/sgml/func.sgml                         |  38 +++--
 doc/src/sgml/ref/create_role.sgml              |  81 +++++++++++
 doc/src/sgml/ref/create_user.sgml              |   6 +
 src/backend/access/transam/xlogfuncs.c         |  27 ++--
 src/backend/catalog/aclchk.c                   | 137 ++++++++++++++++++
 src/backend/commands/user.c                    | 183 ++++++++++++++++++++++---
 src/backend/parser/gram.y                      |  20 +++
 src/backend/replication/logical/logicalfuncs.c |  15 +-
 src/backend/replication/slotfuncs.c            |  25 ++--
 src/backend/replication/walsender.c            |   8 +-
 src/backend/utils/adt/misc.c                   |  19 ++-
 src/backend/utils/adt/pgstatfuncs.c            |  54 ++++++--
 src/backend/utils/init/miscinit.c              |  19 ---
 src/backend/utils/init/postinit.c              |   2 +-
 src/include/catalog/pg_authid.h                |  20 ++-
 src/include/miscadmin.h                        |   1 -
 src/include/utils/acl.h                        |   6 +
 18 files changed, 585 insertions(+), 106 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 515a40e..77b1090 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1454,6 +1454,36 @@
      </row>
 
      <row>
+      <entry><structfield>rolexclbackup</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry>Role can perform on-line exclusive backup operations</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rolxlogreplay</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry>Role can control xlog recovery replay operations</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rollogfile</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry>Role can rotate log files</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rolmonitor</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry>Role can view pg_stat_* details</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rolsignal</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry>Role can signal normal backend processes</entry>
+     </row>
+
+     <row>
       <entry><structfield>rolconnlimit</structfield></entry>
       <entry><type>int4</type></entry>
       <entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index da2ed67..7842cfc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16261,8 +16261,7 @@ SELECT set_config('log_statement_stats', 'off', false);
    <para>
     The functions shown in <xref
     linkend="functions-admin-signal-table"> send control signals to
-    other server processes.  Use of these functions is usually restricted
-    to superusers, with noted exceptions.
+    other server processes.
    </para>
 
    <table id="functions-admin-signal-table">
@@ -16279,9 +16278,11 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_cancel_backend(<parameter>pid</parameter> <type>int</>)</function></literal>
         </entry>
        <entry><type>boolean</type></entry>
-       <entry>Cancel a backend's current query.  You can execute this against
-        another backend that has exactly the same role as the user calling the
-        function.  In all other cases, you must be a superuser.
+       <entry>Cancel a backend's current query.  A user can execute this against
+        another backend which the user owns or which is owned by a role that the user
+        is a member of.  Roles with the SIGNAL attribute can signal all other normal
+        backends except those initiated by superusers.  Superusers can signal all
+        backends.
         </entry>
       </row>
       <row>
@@ -16289,24 +16290,29 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_reload_conf()</function></literal>
         </entry>
        <entry><type>boolean</type></entry>
-       <entry>Cause server processes to reload their configuration files</entry>
+       <entry>Cause server processes to reload their configuration files.  Only a
+        superuser can send this signal.
+       </entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_rotate_logfile()</function></literal>
         </entry>
        <entry><type>boolean</type></entry>
-       <entry>Rotate server's log file</entry>
+       <entry>Rotate server's log file.  Roles with the LOGFILE attribute
+        or who are superusers can send this signal.
+       </entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_terminate_backend(<parameter>pid</parameter> <type>int</>)</function></literal>
         </entry>
        <entry><type>boolean</type></entry>
-       <entry>Terminate a backend.  You can execute this against
-        another backend that has exactly the same role as the user
-        calling the function.  In all other cases, you must be a
-        superuser.
+       <entry>Terminate a backend.  A user can execute this against
+        another backend which the user owns or which is owned by a role that
+        the user is a member of.  Roles with the SIGNAL attribute can signal
+        all other normal backends except those initiated by superuser.
+        Superusers can signal all backends.
        </entry>
       </row>
      </tbody>
@@ -16431,14 +16437,14 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Prepare for performing on-line exclusive backup (restricted to superusers, replication roles, and exclusive backup roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Finish performing on-line exclusive backup (restricted to superusers, replication roles, and exclusive backup roles)</entry>
       </row>
       <row>
        <entry>
@@ -16713,7 +16719,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_pause()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Pauses recovery immediately (restricted to superusers).
+       <entry>Pauses recovery immediately (only users with the XLOGREPLAY
+        attribute and superusers can run this function).
        </entry>
       </row>
       <row>
@@ -16721,7 +16728,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_resume()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Restarts recovery if it was paused (restricted to superusers).
+       <entry>Restarts recovery if it was paused (only users with the
+        XLOGREPLAY attribute and superusers can run this function).
        </entry>
       </row>
      </tbody>
diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index ea26027..cee2c37 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -33,6 +33,11 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
     | LOGIN | NOLOGIN
     | REPLICATION | NOREPLICATION
     | BYPASSRLS | NOBYPASSRLS
+    | EXCLUSIVEBACKUP | NOEXCLUSIVEBACKUP
+    | XLOGREPLAY | NOXLOGREPLAY
+    | LOGFILE | NOLOGFILE
+    | MONITOR | NOMONITOR
+    | SIGNAL | NOSIGNAL
     | CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
     | VALID UNTIL '<replaceable class="PARAMETER">timestamp</replaceable>'
@@ -211,6 +216,82 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
      </varlistentry>
 
      <varlistentry>
+      <term><literal>EXCLUSIVEBACKUP</literal></term>
+      <term><literal>NOEXCLUSIVEBACKUP</literal></term>
+      <listitem>
+       <para>
+       These clauses determine whether a role is allowed to start/stop an
+       on-line exclusive backup.  An on-line exclusive backup is one that is
+       initiated by the low-level base backup interface instead of through the
+       replication protocol, see <xref linkend="backup-lowlevel-base-backup">.
+       If not specified, <literal>NOEXCLUSIVEBACKUP</literal> is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>XLOGREPLAY</literal></term>
+      <term><literal>NOXLOGREPLAY</literal></term>
+      <listitem>
+       <para>
+       These clauses determine whether a role is allowed to pause/resume xlog
+       recovery using the pg_xlog_replay_pause and pg_xlog_replay_resume
+       functions.  See <xref linkend="functions-recovery-control-table"> for
+       details.
+       If not specified, <literal>NOXLOGREPLAY</literal> is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>LOGFILE</literal></term>
+      <term><literal>NOLOGFILE</literal></term>
+      <listitem>
+       <para>
+       These clauses determine whether a role is allowed to rotate log files
+       using the pg_rotate_logfile function.  See <xref
+       linkend="functions-admin-signal"> for details.
+       If not specified, <literal>NOLOGFILE</literal> is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>MONITOR</literal></term>
+      <term><literal>NOMONITOR</literal></term>
+      <listitem>
+       <para>
+       These clauses determine whether a role is allowed to view the details
+       of all processes through the pg_stat_* functions, pg_stat_activity
+       and pg_stat_replication views.  Roles with MONITOR will see the
+       detailed information about all processes, normal and replication, while
+       roles with NOMONITOR will only see the detailed information for processes
+       they own, or which are owned by roles which they are a member of.
+       See <xref linkend="pg-stat-activity-view"> for details.
+       If not specified, <literal>NOMONITOR</literal> is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>SIGNAL</literal></term>
+      <term><literal>NOSIGNAL</literal></term>
+      <listitem>
+       <para>
+       These clauses determine whether a role is allowed to signal other normal
+       backend processes.  A role having the <literal>SIGNAL</literal> attribute
+       will be allowed to signal any backend processes except those initiated by
+       a superuser.  Roles with NOSIGNAL are only allowed to signal processes
+       they own, or which are owned by roles which they are a member of.
+       Signals are able to be sent to normal backends processes using
+       pg_terminate_backend and pg_cancel_backend, see <xref linkend="functions-admin-signal">
+       for further information.
+       If not specified, <literal>NOSIGNAL</literal> is the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><literal>CONNECTION LIMIT</literal> <replaceable class="parameter">connlimit</replaceable></term>
       <listitem>
        <para>
diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml
index 065999c..f7f10c7 100644
--- a/doc/src/sgml/ref/create_user.sgml
+++ b/doc/src/sgml/ref/create_user.sgml
@@ -32,6 +32,12 @@ CREATE USER <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
     | INHERIT | NOINHERIT
     | LOGIN | NOLOGIN
     | REPLICATION | NOREPLICATION
+    | BYPASSRLS | NOBYPASSRLS
+    | EXCLUSIVEBACKUP | NOEXCLUSIVEBACKUP
+    | XLOGREPLAY | NOXLOGREPLAY
+    | LOGFILE | NOLOGFILE
+    | MONITOR | NOMONITOR
+    | SIGNAL | NOSIGNAL
     | CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
     | VALID UNTIL '<replaceable class="PARAMETER">timestamp</replaceable>'
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2179bf7..12b8a17 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -27,6 +27,7 @@
 #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"
@@ -54,10 +55,11 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_replication_privilege(GetUserId())
+		&& !has_exclbackup_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser, replication role or exclusive backup role to run a backup")));
 
 	startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL);
 
@@ -82,10 +84,11 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_replication_privilege(GetUserId())
+		&& !has_exclbackup_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser, replication role or exclusive backup role to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -100,10 +103,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_exclbackup_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or exclusive backup role to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -129,10 +132,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_exclbackup_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 (errmsg("must be superuser or exclusive backup role to create a restore point"))));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -338,10 +341,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_xlog_replay_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 (errmsg("must be superuser or xlog replay role to control recovery"))));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -360,10 +363,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_xlog_replay_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 (errmsg("must be superuser or xlog replay role to control recovery"))));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1e3888e..1d738d6 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -5080,6 +5080,31 @@ has_createrole_privilege(Oid roleid)
 	return result;
 }
 
+/*
+ * Check whether specified role has REPLICATION privilege
+ */
+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
+ */
 bool
 has_bypassrls_privilege(Oid roleid)
 {
@@ -5100,6 +5125,118 @@ has_bypassrls_privilege(Oid roleid)
 }
 
 /*
+ * Check whether specified role has EXCLUSIVEBACKUP privilege
+ */
+bool
+has_exclbackup_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))->rolexclbackup;
+		ReleaseSysCache(utup);
+	}
+
+	return result;
+}
+
+/*
+ * Check whether specified role has XLOGREPLAY privilege
+ */
+bool
+has_xlog_replay_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))->rolxlogreplay;
+		ReleaseSysCache(utup);
+	}
+
+	return result;
+}
+
+/*
+ * Check whether specified role has LOGFILE privilege
+ */
+bool
+has_logfile_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))->rollogfile;
+		ReleaseSysCache(utup);
+	}
+	return result;
+}
+
+/*
+ * Check whether specified role has MONITOR privilege
+ */
+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 SIGNAL privilege
+ */
+bool
+has_signal_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))->rolsignal;
+		ReleaseSysCache(utup);
+	}
+	return result;
+}
+
+/*
  * Fetch pg_default_acl entry for given role, namespace and object type
  * (object type must be given in pg_default_acl's encoding).
  * Returns NULL if no such entry.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 2210eed..e0eb5c3 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -55,15 +55,6 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			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
  */
@@ -88,6 +79,11 @@ CreateRole(CreateRoleStmt *stmt)
 	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		exclbackup = false;
+	bool		xlogreplay = false;
+	bool		logfile = false;
+	bool		monitor = false;
+	bool		signal = 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 */
@@ -108,6 +104,11 @@ CreateRole(CreateRoleStmt *stmt)
 	DefElem    *dadminmembers = NULL;
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
+	DefElem    *dexclbackup = NULL;
+	DefElem    *dxlogreplay = NULL;
+	DefElem    *dlogfile = NULL;
+	DefElem    *dmonitor = NULL;
+	DefElem    *dsignal = NULL;
 
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
@@ -242,6 +243,46 @@ CreateRole(CreateRoleStmt *stmt)
 						 errmsg("conflicting or redundant options")));
 			dbypassRLS = defel;
 		}
+		else if (strcmp(defel->defname, "exclbackup") == 0)
+		{
+			if (dexclbackup)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dexclbackup = defel;
+		}
+		else if (strcmp(defel->defname, "xlogreplay") == 0)
+		{
+			if (dxlogreplay)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dxlogreplay = defel;
+		}
+		else if (strcmp(defel->defname, "logfile") == 0)
+		{
+			if (dlogfile)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dlogfile = 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, "signal") == 0)
+		{
+			if (dsignal)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dsignal = defel;
+		}
 		else
 			elog(ERROR, "option \"%s\" not recognized",
 				 defel->defname);
@@ -279,6 +320,16 @@ CreateRole(CreateRoleStmt *stmt)
 		validUntil = strVal(dvalidUntil->arg);
 	if (dbypassRLS)
 		bypassrls = intVal(dbypassRLS->arg) != 0;
+	if (dexclbackup)
+		exclbackup = intVal(dexclbackup->arg) != 0;
+	if (dxlogreplay)
+		xlogreplay = intVal(dxlogreplay->arg) != 0;
+	if (dlogfile)
+		logfile = intVal(dlogfile->arg) != 0;
+	if (dmonitor)
+		monitor = intVal(dmonitor->arg) != 0;
+	if (dsignal)
+		signal = intVal(dsignal->arg) != 0;
 
 	/* Check some permissions first */
 	if (issuper)
@@ -304,7 +355,7 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 	else
 	{
-		if (!have_createrole_privilege())
+		if (!has_createrole_privilege(GetUserId()))
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("permission denied to create role")));
@@ -395,6 +446,11 @@ CreateRole(CreateRoleStmt *stmt)
 	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
 
 	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
+	new_record[Anum_pg_authid_rolexclbackup - 1] = BoolGetDatum(exclbackup);
+	new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay);
+	new_record[Anum_pg_authid_rollogfile - 1] = BoolGetDatum(logfile);
+	new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor);
+	new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal);
 
 	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
 
@@ -496,6 +552,11 @@ AlterRole(AlterRoleStmt *stmt)
 	Datum		validUntil_datum;		/* same, as timestamptz Datum */
 	bool		validUntil_null;
 	bool		bypassrls = -1;
+	bool		exclbackup = -1;
+	bool		xlogreplay = -1;
+	bool		logfile = -1;
+	bool		monitor = -1;
+	bool		signal = -1;
 	DefElem    *dpassword = NULL;
 	DefElem    *dissuper = NULL;
 	DefElem    *dinherit = NULL;
@@ -507,6 +568,11 @@ AlterRole(AlterRoleStmt *stmt)
 	DefElem    *drolemembers = NULL;
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
+	DefElem    *dexclbackup = NULL;
+	DefElem    *dxlogreplay = NULL;
+	DefElem    *dlogfile = NULL;
+	DefElem    *dmonitor = NULL;
+	DefElem    *dsignal = NULL;
 	Oid			roleid;
 
 	/* Extract options from the statement node tree */
@@ -609,6 +675,46 @@ AlterRole(AlterRoleStmt *stmt)
 						 errmsg("conflicting or redundant options")));
 			dbypassRLS = defel;
 		}
+		else if (strcmp(defel->defname, "exclbackup") == 0)
+		{
+			if (dexclbackup)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dexclbackup = defel;
+		}
+		else if (strcmp(defel->defname, "xlogreplay") == 0)
+		{
+			if (dxlogreplay)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dxlogreplay = defel;
+		}
+		else if (strcmp(defel->defname, "logfile") == 0)
+		{
+			if (dlogfile)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dlogfile = 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, "signal") == 0)
+		{
+			if (dsignal)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dsignal = defel;
+		}
 		else
 			elog(ERROR, "option \"%s\" not recognized",
 				 defel->defname);
@@ -642,6 +748,16 @@ AlterRole(AlterRoleStmt *stmt)
 		validUntil = strVal(dvalidUntil->arg);
 	if (dbypassRLS)
 		bypassrls = intVal(dbypassRLS->arg);
+	if (dexclbackup)
+		exclbackup = intVal(dexclbackup->arg);
+	if (dxlogreplay)
+		xlogreplay = intVal(dxlogreplay->arg);
+	if (dlogfile)
+		logfile = intVal(dlogfile->arg);
+	if (dmonitor)
+		monitor = intVal(dmonitor->arg);
+	if (dsignal)
+		signal = intVal(dsignal->arg);
 
 	/*
 	 * Scan the pg_authid relation to be certain the user exists.
@@ -682,13 +798,18 @@ AlterRole(AlterRoleStmt *stmt)
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("must be superuser to change bypassrls attribute")));
 	}
-	else if (!have_createrole_privilege())
+	else if (!has_createrole_privilege(GetUserId()))
 	{
 		if (!(inherit < 0 &&
 			  createrole < 0 &&
 			  createdb < 0 &&
 			  canlogin < 0 &&
 			  isreplication < 0 &&
+			  exclbackup < 0 &&
+			  xlogreplay < 0 &&
+			  logfile < 0 &&
+			  monitor < 0 &&
+			  signal < 0 &&
 			  !dconnlimit &&
 			  !rolemembers &&
 			  !validUntil &&
@@ -821,6 +942,36 @@ AlterRole(AlterRoleStmt *stmt)
 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
 	}
 
+	if (exclbackup >= 0)
+	{
+		new_record[Anum_pg_authid_rolexclbackup - 1] = BoolGetDatum(exclbackup > 0);
+		new_record_repl[Anum_pg_authid_rolexclbackup - 1] = true;
+	}
+
+	if (xlogreplay >= 0)
+	{
+		new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay > 0);
+		new_record_repl[Anum_pg_authid_rolxlogreplay - 1] = true;
+	}
+
+	if (logfile >= 0)
+	{
+		new_record[Anum_pg_authid_rollogfile - 1] = BoolGetDatum(logfile > 0);
+		new_record_repl[Anum_pg_authid_rollogfile - 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 (signal >= 0)
+	{
+		new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal > 0);
+		new_record_repl[Anum_pg_authid_rolsignal - 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);
@@ -898,7 +1049,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
 		}
 		else
 		{
-			if (!have_createrole_privilege() &&
+			if (!has_createrole_privilege(GetUserId()) &&
 				HeapTupleGetOid(roletuple) != GetUserId())
 				ereport(ERROR,
 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -951,7 +1102,7 @@ DropRole(DropRoleStmt *stmt)
 				pg_auth_members_rel;
 	ListCell   *item;
 
-	if (!have_createrole_privilege())
+	if (!has_createrole_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to drop role")));
@@ -1182,7 +1333,7 @@ RenameRole(const char *oldname, const char *newname)
 	}
 	else
 	{
-		if (!have_createrole_privilege())
+		if (!has_createrole_privilege(GetUserId()))
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("permission denied to rename role")));
@@ -1409,7 +1560,7 @@ AddRoleMems(const char *rolename, Oid roleid,
 	}
 	else
 	{
-		if (!have_createrole_privilege() &&
+		if (!has_createrole_privilege(GetUserId()) &&
 			!is_admin_of_role(grantorId, roleid))
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -1555,7 +1706,7 @@ DelRoleMems(const char *rolename, Oid roleid,
 	}
 	else
 	{
-		if (!have_createrole_privilege() &&
+		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
index 581f7a1..ead0ce2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -978,6 +978,26 @@ AlterOptRoleElem:
 						 */
 						$$ = makeDefElem("inherit", (Node *)makeInteger(FALSE));
 					}
+					else if (strcmp($1, "exclusivebackup") == 0)
+						$$ = makeDefElem("exclbackup", (Node *)makeInteger(TRUE));
+					else if (strcmp($1, "noexclusivebackup") == 0)
+						$$ = makeDefElem("exclbackup", (Node *)makeInteger(FALSE));
+					else if (strcmp($1, "xlogreplay") == 0)
+						$$ = makeDefElem("xlogreplay", (Node *)makeInteger(TRUE));
+					else if (strcmp($1, "noxlogreplay") == 0)
+						$$ = makeDefElem("xlogreplay", (Node *)makeInteger(FALSE));
+					else if (strcmp($1, "logfile") == 0)
+						$$ = makeDefElem("logfile", (Node *)makeInteger(TRUE));
+					else if (strcmp($1, "nologfile") == 0)
+						$$ = makeDefElem("logfile", (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, "signal") == 0)
+						$$ = makeDefElem("signal", (Node *)makeInteger(TRUE));
+					else if (strcmp($1, "nosignal") == 0)
+						$$ = makeDefElem("signal", (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
index 3be5263..b7e7947 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -29,6 +29,7 @@
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +203,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +316,10 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	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
index f31925d..35e1c11 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -20,18 +20,10 @@
 #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"
 
-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.
@@ -51,7 +43,10 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_replication_privilege(GetUserId()))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or replication role to use replication slots")));
 
 	CheckSlotRequirements();
 
@@ -94,7 +89,10 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_replication_privilege(GetUserId()))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or replication role to use replication slots")));
 
 	CheckLogicalDecodingRequirements();
 
@@ -143,7 +141,10 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	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
index af5c1cc..ee8b6f9 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -71,6 +71,7 @@
 #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"
@@ -2794,11 +2795,12 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_monitor_privilege(GetUserId()))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * 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/misc.c b/src/backend/utils/adt/misc.c
index 29f7c3b..0c1c31a 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -37,6 +37,7 @@
 #include "utils/lsyscache.h"
 #include "utils/ruleutils.h"
 #include "tcop/tcopprot.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/timestamp.h"
 
@@ -113,7 +114,19 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_ERROR;
 	}
 
-	if (!(superuser() || proc->roleId == GetUserId()))
+	/*
+	 * 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 SIGNAL permission, then permission is denied.
+	 */
+	if (!has_privs_of_role(GetUserId(), proc->roleId)
+		&& !has_signal_privilege(GetUserId()))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -202,10 +215,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_logfile_privilege(GetUserId()))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 errmsg("must be superuser or have logfile permission to rotate log files")));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 9964c5e..aae4d0d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -20,6 +20,7 @@
 #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"
@@ -626,6 +627,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		HeapTuple	tuple;
 		LocalPgBackendStatus *local_beentry;
 		PgBackendStatus *beentry;
+		Oid			current_user_id;
 
 		MemSet(values, 0, sizeof(values));
 		MemSet(nulls, 0, sizeof(nulls));
@@ -675,8 +677,14 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		else
 			nulls[15] = true;
 
-		/* Values only available to same user or superuser */
-		if (superuser() || beentry->st_userid == GetUserId())
+		/*
+		 * 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;
 
@@ -875,10 +883,14 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 	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 (!superuser() && beentry->st_userid != GetUserId())
+	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>";
@@ -895,11 +907,15 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	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();
 
-	if (!superuser() && beentry->st_userid != GetUserId())
+	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;
@@ -914,11 +930,15 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	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();
 
-	if (!superuser() && beentry->st_userid != GetUserId())
+	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;
@@ -940,11 +960,15 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	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();
 
-	if (!superuser() && beentry->st_userid != GetUserId())
+	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;
@@ -962,11 +986,15 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	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();
 
-	if (!superuser() && beentry->st_userid != GetUserId())
+	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;
@@ -986,11 +1014,15 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	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();
 
-	if (!superuser() && beentry->st_userid != GetUserId())
+	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 */
@@ -1033,11 +1065,15 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	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();
 
-	if (!superuser() && beentry->st_userid != GetUserId())
+	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
index 1dc3153..bb43b62 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -430,25 +430,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
 		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
  */
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 1e646a1..8cde310 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -765,7 +765,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	{
 		Assert(!bootstrap);
 
-		if (!superuser() && !has_rolreplication(GetUserId()))
+		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
index b3f43e1..c2128b8 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -53,6 +53,11 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC
 	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		rolexclbackup;	/* allowed to peform backup operations? */
+	bool		rolxlogreplay;	/* allowed to control xlog replay */
+	bool		rollogfile;		/* allowed to rotate log files? */
+	bool		rolmonitor;		/* allowed to view pg_stat_* details? */
+	bool		rolsignal;		/* allowed to signal backed processes? */
 	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
 
 	/* remaining fields may be null; use heap_getattr to read them! */
@@ -76,7 +81,7 @@ typedef FormData_pg_authid *Form_pg_authid;
  *		compiler constants for pg_authid
  * ----------------
  */
-#define Natts_pg_authid					12
+#define Natts_pg_authid					17
 #define Anum_pg_authid_rolname			1
 #define Anum_pg_authid_rolsuper			2
 #define Anum_pg_authid_rolinherit		3
@@ -86,9 +91,14 @@ typedef FormData_pg_authid *Form_pg_authid;
 #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
+#define Anum_pg_authid_rolexclbackup	10
+#define Anum_pg_authid_rolxlogreplay	11
+#define Anum_pg_authid_rollogfile		12
+#define Anum_pg_authid_rolmonitor		13
+#define Anum_pg_authid_rolsignal		14
+#define Anum_pg_authid_rolconnlimit		15
+#define Anum_pg_authid_rolpassword		16
+#define Anum_pg_authid_rolvaliduntil	17
 
 /* ----------------
  *		initial contents of pg_authid
@@ -97,7 +107,7 @@ typedef FormData_pg_authid *Form_pg_authid;
  * user choices.
  * ----------------
  */
-DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 10 ( "POSTGRES" t 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
index eacfccb..ec516d9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -454,7 +454,6 @@ extern void ValidatePgVersion(const char *path);
 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);
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index ab0df6c..63c702b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -328,5 +328,11 @@ extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
 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_exclbackup_privilege(Oid roleid);
+extern bool has_xlog_replay_privilege(Oid roleid);
+extern bool has_logfile_privilege(Oid roleid);
+extern bool has_monitor_privilege(Oid roleid);
+extern bool has_signal_privilege(Oid roleid);
 
 #endif   /* ACL_H */
-- 
1.9.1

#83Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#82)
Re: Additional role attributes && superuser review

Stephen Frost wrote:

Thanks! I've gone over this and made quite a few documentation and
comment updates, but not too much else, so I'm pretty happy with how
this is coming along. As mentioned elsewhere, this conflicts with the
GetUserId() to has_privs_of_role() cleanup, but as I anticipate handling
both this patch and that one, I'll find some way to manage. :)

Updated patch attached. Barring objections, I'll be moving forward with
this soonish. Would certainly appreciate any additional testing or
review that you (or anyone!) has time to provide.

I thought I saw a comment about using underscore to separate words in
privilege names, such as EXCLUSIVE_BACKUP rather than running it all
together. Was that idea discarded?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#84Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#83)
Re: Additional role attributes && superuser review

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

Thanks! I've gone over this and made quite a few documentation and
comment updates, but not too much else, so I'm pretty happy with how
this is coming along. As mentioned elsewhere, this conflicts with the
GetUserId() to has_privs_of_role() cleanup, but as I anticipate handling
both this patch and that one, I'll find some way to manage. :)

Updated patch attached. Barring objections, I'll be moving forward with
this soonish. Would certainly appreciate any additional testing or
review that you (or anyone!) has time to provide.

I thought I saw a comment about using underscore to separate words in
privilege names, such as EXCLUSIVE_BACKUP rather than running it all
together. Was that idea discarded?

Hrm, I'm not finding that on a quick look through the archives. Looking
at the other role options, we don't have any with underscores but we do
have a couple of cases where we uses spaces, eg:

CONNECTION LIMIT
VALID UNTIL

So, using EXCLUSIVE BACKUP would be similar to those, but we'd then need
to have the 'NO' version as 'NO EXCLUSIVE BACKUP', I'd think, and I'm
not sure we really want to go there.

On the other hand, we do run together the 'CREATE' options (CREATEDB,
CREATEROLE), and I've not heard anyone suggesting to change those.

I guess for my part, at least, having it run together as
'EXCLUSIVEBACKUP' seems fine, but this does make me realize that I need
to go add tab-completion for these new options. :)

Thanks!

Stephen

#85Adam Brightwell
adam.brightwell@crunchydatasolutions.com
In reply to: Alvaro Herrera (#83)
Re: Additional role attributes && superuser review

Alvaro,

I thought I saw a comment about using underscore to separate words in

privilege names, such as EXCLUSIVE_BACKUP rather than running it all
together. Was that idea discarded?

I'm not sure there was an actual discussion on the topic. Though, at one
point I had proposed it as one of the forms of this attribute. Personally,
I think it is easier to read with the underscore. But, ultimately, I
defaulted to no underscore to remain consistent with the other attributes,
such as CREATEDB and CREATEROLE.

Thanks,
Adam

--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com

#86Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Adam Brightwell (#85)
Re: Additional role attributes && superuser review

Adam Brightwell wrote:

Alvaro,

I thought I saw a comment about using underscore to separate words
in privilege names, such as EXCLUSIVE_BACKUP rather than running it
all together. Was that idea discarded?

I'm not sure there was an actual discussion on the topic. Though, at one
point I had proposed it as one of the forms of this attribute. Personally,
I think it is easier to read with the underscore. But, ultimately, I
defaulted to no underscore to remain consistent with the other attributes,
such as CREATEDB and CREATEROLE.

If we were choosing those names nowadays, would we choose CREATEDB at
all in the first place? I think we'd go for something more verbose,
probably CREATE_DATABASE. (CREATEROLE is not as old as CREATEDB, but my
bet is that it was modelled after CREATEUSER without considering the
whole readability topic too much.)

Anyway it doesn't seem to me that consistency with lack of separators in
those very old names should be our guiding principle here.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#87Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#86)
Re: Additional role attributes && superuser review

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Adam Brightwell wrote:

I'm not sure there was an actual discussion on the topic. Though, at one
point I had proposed it as one of the forms of this attribute. Personally,
I think it is easier to read with the underscore. But, ultimately, I
defaulted to no underscore to remain consistent with the other attributes,
such as CREATEDB and CREATEROLE.

If we were choosing those names nowadays, would we choose CREATEDB at
all in the first place? I think we'd go for something more verbose,
probably CREATE_DATABASE. (CREATEROLE is not as old as CREATEDB, but my
bet is that it was modelled after CREATEUSER without considering the
whole readability topic too much.)

Anyway it doesn't seem to me that consistency with lack of separators in
those very old names should be our guiding principle here.

So you'd advocate EXCLUSIVE_BACKUP and NOEXCLUSIVE_BACKUP? Or
NO_EXCLUSIVE_BACKUP? Or..? If this was a green field, I think we might
actually use spaces instead, but I'm really not sure we want to go
through and redo everything that way at this point.. We'd end up
breaking a lot of scripts that currently work today and I'm really not
convinced it's better enough to justify that.

Thanks!

Stephen

#88Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#87)
Re: Additional role attributes && superuser review

Stephen Frost <sfrost@snowman.net> writes:

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

If we were choosing those names nowadays, would we choose CREATEDB at
all in the first place? I think we'd go for something more verbose,
probably CREATE_DATABASE. (CREATEROLE is not as old as CREATEDB, but my
bet is that it was modelled after CREATEUSER without considering the
whole readability topic too much.)

Anyway it doesn't seem to me that consistency with lack of separators in
those very old names should be our guiding principle here.

So you'd advocate EXCLUSIVE_BACKUP and NOEXCLUSIVE_BACKUP? Or
NO_EXCLUSIVE_BACKUP? Or..? If this was a green field, I think we might
actually use spaces instead, but I'm really not sure we want to go
through and redo everything that way at this point.. We'd end up
breaking a lot of scripts that currently work today and I'm really not
convinced it's better enough to justify that.

FWIW, I agree with Alvaro, and I'd go with e.g. NO_EXCLUSIVE_BACKUP.

I concur that multiple separate words would be a syntax mess, but I
see no reason not to use underscores instead.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#89Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#88)
Re: Additional role attributes && superuser review

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

If we were choosing those names nowadays, would we choose CREATEDB at
all in the first place? I think we'd go for something more verbose,
probably CREATE_DATABASE. (CREATEROLE is not as old as CREATEDB, but my
bet is that it was modelled after CREATEUSER without considering the
whole readability topic too much.)

Anyway it doesn't seem to me that consistency with lack of separators in
those very old names should be our guiding principle here.

So you'd advocate EXCLUSIVE_BACKUP and NOEXCLUSIVE_BACKUP? Or
NO_EXCLUSIVE_BACKUP? Or..? If this was a green field, I think we might
actually use spaces instead, but I'm really not sure we want to go
through and redo everything that way at this point.. We'd end up
breaking a lot of scripts that currently work today and I'm really not
convinced it's better enough to justify that.

FWIW, I agree with Alvaro, and I'd go with e.g. NO_EXCLUSIVE_BACKUP.

Ok, works for me. Will update (including tab completion) and send out
for review.

Thanks!

Stephen

#90Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#87)
Re: Additional role attributes && superuser review

Stephen Frost wrote:

So you'd advocate EXCLUSIVE_BACKUP and NOEXCLUSIVE_BACKUP? Or
NO_EXCLUSIVE_BACKUP? Or..? If this was a green field, I think we might
actually use spaces instead, but I'm really not sure we want to go
through and redo everything that way at this point.. We'd end up
breaking a lot of scripts that currently work today and I'm really not
convinced it's better enough to justify that.

Well, all things considered, we actually are in a green field here,
aren't we. We don't have to break old scripts, but no existing script
is using
ALTER USER foo NOEXCLUSIVEBACKUP
because that option doesn't currently exist.

That being so, I would consider the idea that the NO bit is a separate
word rather than run together with the actual privilege name. And given
that CREATE has all the options default to "NO", there is no need to
have these options at all in CREATE, is there?

So we would have only the "positive" actions in create:

CREATE USER foo REPLICATION EXCLUSIVE_BACKUP
(if you want no EXCLUSIVE_BACKUP privilege, just leave it out. It isn't
any more complicated than saying NOEXCLUSIVEBACKUP)

You could turn these off in ALTER:

ALTER USER foo NO EXCLUSIVE_BACKUP;
or perhaps
ALTER USER foo DROP EXCLUSIVE_BACKUP;
or some such.

(REVOKE would be nicer, I guess, but IIRC that conflicts with other
stuff. I guess another option, in line with the current optional WITH,
would be to have WITHOUT:
ALTER USER foo WITHOUT EXCLUSIVE_BACKUP;
But that looks a bit silly to me.)

Then things such as
ALTER USER foo NOREPLICATION
become synonyms for
ALTER USER foo NO REPLICATION (or whatever)

Something like that would be my choice for UI, anyway. The existing
stuff seems to clutter it overly, and while it works sorta OK for half a
dozen privs, it becomes clunkier as you have more of them. From the
perspective of docs, I think this whole thing becomes simpler if you
split out the NO from each privilege name; currently we have

alvherre=# \h alter user
Command: ALTER USER
Description: change a database role
Syntax:
ALTER USER name [ [ WITH ] option [ ... ] ]

where option can be:

SUPERUSER | NOSUPERUSER
| CREATEDB | NOCREATEDB
| CREATEROLE | NOCREATEROLE
| CREATEUSER | NOCREATEUSER
| INHERIT | NOINHERIT
| LOGIN | NOLOGIN
| REPLICATION | NOREPLICATION
| CONNECTION LIMIT connlimit
| [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'

And it would become something like

ALTER USER name [ WITH ] { [ NO ] privilege | option } [ ... ]

where privilege can be:
SUPERUSER | CREATEDB | CREATEROLE
| CREATEUSER | INHERIT | LOGIN
| REPLICATION | EXCLUSIVE_BACKUP | ...
and option can be:
CONNECTION LIMIT connlimit
| [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'

(the NOSUPERUSER etc forms of the old options would continue to work for
the sake of backwards compatibility, but we wouldn't provide NO-
prefixed forms of the new privileges.)

I'm not wedded to any of this, but I think it ought to be at least given
some consideration.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#91Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#90)
Re: Additional role attributes && superuser review

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

So you'd advocate EXCLUSIVE_BACKUP and NOEXCLUSIVE_BACKUP? Or
NO_EXCLUSIVE_BACKUP? Or..? If this was a green field, I think we might
actually use spaces instead, but I'm really not sure we want to go
through and redo everything that way at this point.. We'd end up
breaking a lot of scripts that currently work today and I'm really not
convinced it's better enough to justify that.

Well, all things considered, we actually are in a green field here,
aren't we. We don't have to break old scripts, but no existing script
is using
ALTER USER foo NOEXCLUSIVEBACKUP
because that option doesn't currently exist.

Sorry for not being clear, I was talking about how we'd deal with the
existing ones (to try and have a consistent approach across all of the
options).

That being so, I would consider the idea that the NO bit is a separate
word rather than run together with the actual privilege name. And given
that CREATE has all the options default to "NO", there is no need to
have these options at all in CREATE, is there?

That's a good point, except that INHERIT is actually on by default, and
LOGIN defaults to 'on' if you use CREATE USER, and 'off' if you use
CREATE ROLE. If they were actually all 'no' by default then this
simplication would work but it's not and therefore I don't think we want
to have some which are allowed at CREATE time with 'on' and some with
'off' depending on whatever the default is. Today, you can write a
script to easily duplicate an existing role by just looking at what is
on and off and using X and NOX. This approach would require that script
to know what's valid at CREATE time and what isn't.

Then things such as
ALTER USER foo NOREPLICATION
become synonyms for
ALTER USER foo NO REPLICATION (or whatever)

I'm not against supporting this and if it's a synonym then it won't
break existing scripts. I can look into this, but it ends up being an
independent discussion from actually adding these new options.

Something like that would be my choice for UI, anyway. The existing
stuff seems to clutter it overly, and while it works sorta OK for half a
dozen privs, it becomes clunkier as you have more of them. From the
perspective of docs, I think this whole thing becomes simpler if you
split out the NO from each privilege name; currently we have

I agree that it does get clunkier and this is certainly an interesting
discussion as we might be able to do something better, which would
certainly be great.

alvherre=# \h alter user
Command: ALTER USER
Description: change a database role
Syntax:
ALTER USER name [ [ WITH ] option [ ... ] ]

where option can be:

SUPERUSER | NOSUPERUSER
| CREATEDB | NOCREATEDB
| CREATEROLE | NOCREATEROLE
| CREATEUSER | NOCREATEUSER
| INHERIT | NOINHERIT
| LOGIN | NOLOGIN
| REPLICATION | NOREPLICATION
| CONNECTION LIMIT connlimit
| [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'

And it would become something like

ALTER USER name [ WITH ] { [ NO ] privilege | option } [ ... ]

where privilege can be:
SUPERUSER | CREATEDB | CREATEROLE
| CREATEUSER | INHERIT | LOGIN
| REPLICATION | EXCLUSIVE_BACKUP | ...
and option can be:
CONNECTION LIMIT connlimit
| [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'

With the 'NO' distinct, as discussed above, it seems like we should be
able to support this.. I certainly like it more too.

(the NOSUPERUSER etc forms of the old options would continue to work for
the sake of backwards compatibility, but we wouldn't provide NO-
prefixed forms of the new privileges.)

I'm not sure that I like that.. My initial reaction is that it'd
complicate the code and confuse people coming to this later- and to what
purpose..? To force everyone to use 'NO<space>' instead? Maybe in some
far off future we could say that the old 'NOX' format was deprecated and
remove it, but I have a hard time seeing the need to.

I'm not wedded to any of this, but I think it ought to be at least given
some consideration.

Certainly, and I like where you're going with this, just seems like
there's a couple wrinkles to deal with.

Thanks!

Stephen

#92Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#90)
Re: Additional role attributes && superuser review

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

That being so, I would consider the idea that the NO bit is a separate
word rather than run together with the actual privilege name. And given
that CREATE has all the options default to "NO", there is no need to
have these options at all in CREATE, is there?

FWIW, I disagree with that, mainly because I don't think we should cast in
stone the assumption that NO will always be the default for everything we
might invent in the future. Also, the precedent of the existing options
will lead people to expect that they can explicitly say NO-whatever.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#93Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#92)
Re: Additional role attributes && superuser review

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

That being so, I would consider the idea that the NO bit is a separate
word rather than run together with the actual privilege name. And given
that CREATE has all the options default to "NO", there is no need to
have these options at all in CREATE, is there?

FWIW, I disagree with that, mainly because I don't think we should cast in
stone the assumption that NO will always be the default for everything we
might invent in the future. Also, the precedent of the existing options
will lead people to expect that they can explicitly say NO-whatever.

Right, and, in fact, not everything is 'NO' by default today anyway.

Further, people might try to say 'NO CONNECTION LIMIT', and while we
might want to try and support that, there might be options where 'NO'
doesn't actually make sense ('NO VALID UNTIL'?).

Thanks!

Stephen

#94Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#91)
Re: Additional role attributes && superuser review

Stephen Frost wrote:

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

That being so, I would consider the idea that the NO bit is a separate
word rather than run together with the actual privilege name. And given
that CREATE has all the options default to "NO", there is no need to
have these options at all in CREATE, is there?

That's a good point, except that INHERIT is actually on by default, and
LOGIN defaults to 'on' if you use CREATE USER, and 'off' if you use
CREATE ROLE. If they were actually all 'no' by default then this
simplication would work but it's not and therefore I don't think we want
to have some which are allowed at CREATE time with 'on' and some with
'off' depending on whatever the default is. Today, you can write a
script to easily duplicate an existing role by just looking at what is
on and off and using X and NOX. This approach would require that script
to know what's valid at CREATE time and what isn't.

All true.

where privilege can be:
SUPERUSER | CREATEDB | CREATEROLE
| CREATEUSER | INHERIT | LOGIN
| REPLICATION | EXCLUSIVE_BACKUP | ...
and option can be:
CONNECTION LIMIT connlimit
| [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'

With the 'NO' distinct, as discussed above, it seems like we should be
able to support this.. I certainly like it more too.

Great.

Certainly, and I like where you're going with this, just seems like
there's a couple wrinkles to deal with.

Yeah.

Let's go with the "NO_" prefix then ... that seems better to me than no
separator.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#95Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#94)
Re: Additional role attributes && superuser review

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Let's go with the "NO_" prefix then ... that seems better to me than no
separator.

Works for me.

Thanks!

Stephen

#96Peter Eisentraut
peter_e@gmx.net
In reply to: Stephen Frost (#82)
Re: Additional role attributes && superuser review

On 2/28/15 10:10 PM, Stephen Frost wrote:

Adam,

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

I have attached and updated patch for review.

Thanks! I've gone over this and made quite a few documentation and
comment updates, but not too much else, so I'm pretty happy with how
this is coming along. As mentioned elsewhere, this conflicts with the
GetUserId() to has_privs_of_role() cleanup, but as I anticipate handling
both this patch and that one, I'll find some way to manage. :)

Updated patch attached. Barring objections, I'll be moving forward with
this soonish. Would certainly appreciate any additional testing or
review that you (or anyone!) has time to provide.

Let's move this discussion to the right thread.

Why are we not using roles and function execute privileges for this?

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#97Stephen Frost
sfrost@snowman.net
In reply to: Peter Eisentraut (#96)
Re: Additional role attributes && superuser review

* Peter Eisentraut (peter_e@gmx.net) wrote:

On 2/28/15 10:10 PM, Stephen Frost wrote:

* Adam Brightwell (adam.brightwell@crunchydatasolutions.com) wrote:

I have attached and updated patch for review.

Thanks! I've gone over this and made quite a few documentation and
comment updates, but not too much else, so I'm pretty happy with how
this is coming along. As mentioned elsewhere, this conflicts with the
GetUserId() to has_privs_of_role() cleanup, but as I anticipate handling
both this patch and that one, I'll find some way to manage. :)

Updated patch attached. Barring objections, I'll be moving forward with
this soonish. Would certainly appreciate any additional testing or
review that you (or anyone!) has time to provide.

Let's move this discussion to the right thread.

Agreed.

Why are we not using roles and function execute privileges for this?

There isn't a particular reason not to, except that the existing checks
are in C code and those would need to be removed and the permission
changes done at initdb time to revoke EXECUTE from PUBLIC for these
functions. Further, as you pointed out, we'd need to dump out the
permissions for the catalog tables and functions with this approach. I
don't expect that to be too difficult to do though.

Thanks!

Stephen

#98Stephen Frost
sfrost@snowman.net
In reply to: Peter Eisentraut (#96)
1 attachment(s)
Re: Additional role attributes && superuser review

Peter, all,

* Peter Eisentraut (peter_e@gmx.net) wrote:

Why are we not using roles and function execute privileges for this?

Alright, I've got an initial patch to do this for pg_start/stop_backup,
pg_switch_xlog, and pg_create_restore_point. The actual backend changes
are quite small, as expected. I'll add in the changes for the other
functions being discussed and adapt the documentation changes from
the earlier patch to make sense, but what I'd really appreciate are any
comments or thoughts regarding the changes to pg_dump (which are generic
to all of the function changes, of course).

I've added a notion of "the catalog schema" to pg_dump's internal
_namespaceinfo representation and then marked pg_catalog as being that
schema, as well as being a "dumpable" schema. Throughout the
selectDumpable functions, I've made changes to only mark the objects in
the catalog as dumpable if they are functions. I'm planning to look
into the extension and binary upgrade paths since I'm a bit worried
those may not work with this approach, but I wanted to get this out
there for at least an initial review as, if people feel this makes
things too ugly on the pg_dump side of things then we may want to
reconsider using the role attributes instead.

Thanks!

Stephen

Attachments:

catalog_function_acls.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 2179bf7..36029d0
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
*************** pg_start_backup(PG_FUNCTION_ARGS)
*** 54,64 ****
  
  	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);
  
  	PG_RETURN_LSN(startpoint);
--- 54,59 ----
*************** pg_stop_backup(PG_FUNCTION_ARGS)
*** 82,92 ****
  {
  	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);
  
  	PG_RETURN_LSN(stoppoint);
--- 77,82 ----
*************** pg_switch_xlog(PG_FUNCTION_ARGS)
*** 100,110 ****
  {
  	XLogRecPtr	switchpoint;
  
- 	if (!superuser())
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 			 (errmsg("must be superuser to switch transaction log files"))));
- 
  	if (RecoveryInProgress())
  		ereport(ERROR,
  				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
--- 90,95 ----
*************** pg_create_restore_point(PG_FUNCTION_ARGS
*** 129,139 ****
  	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,
  				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
--- 114,119 ----
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index 2800f73..38605de
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** RETURNS interval
*** 897,899 ****
--- 897,907 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'make_interval';
+ 
+ -- Revoke privileges for functions that should not be available to
+ -- all users.  Administrators are allowed to change this later, if
+ -- they wish.
+ REVOKE EXECUTE ON FUNCTION pg_start_backup(text, boolean) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_stop_backup() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_switch_xlog() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_create_restore_point(text) FROM public;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index fdfb431..164608a
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** selectDumpableNamespace(NamespaceInfo *n
*** 1234,1245 ****
--- 1234,1255 ----
  	 * If specific tables are being dumped, do not dump any complete
  	 * namespaces. If specific namespaces are being dumped, dump just those
  	 * namespaces. Otherwise, dump all non-system namespaces.
+ 	 *
+ 	 * Note that we do consider dumping ACLs of functions in pg_catalog,
+ 	 * so mark that as a dumpable namespace, but further mark it as the
+ 	 * catalog namespace.
  	 */
+ 
+ 	/* Will be set when we may be dumping catalog ACLs, see below. */
+ 	nsinfo->catalog = false;
+ 
  	if (table_include_oids.head != NULL)
  		nsinfo->dobj.dump = false;
  	else if (schema_include_oids.head != NULL)
  		nsinfo->dobj.dump = simple_oid_list_member(&schema_include_oids,
  												   nsinfo->dobj.catId.oid);
+ 	else if (strncmp(nsinfo->dobj.name, "pg_catalog", 10) == 0)
+ 		nsinfo->dobj.dump = nsinfo->catalog = true;
  	else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
  			 strcmp(nsinfo->dobj.name, "information_schema") == 0)
  		nsinfo->dobj.dump = false;
*************** selectDumpableTable(TableInfo *tbinfo)
*** 1264,1276 ****
  {
  	/*
  	 * If specific tables are being dumped, dump just those tables; else, dump
! 	 * according to the parent namespace's dump flag.
  	 */
  	if (table_include_oids.head != NULL)
  		tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
  												   tbinfo->dobj.catId.oid);
  	else
! 		tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump;
  
  	/*
  	 * In any case, a table can be excluded by an exclusion switch
--- 1274,1288 ----
  {
  	/*
  	 * If specific tables are being dumped, dump just those tables; else, dump
! 	 * according to the parent namespace's dump flag, except we never dump
! 	 * catalog tables.
  	 */
  	if (table_include_oids.head != NULL)
  		tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
  												   tbinfo->dobj.catId.oid);
  	else
! 		tbinfo->dobj.dump = tbinfo->dobj.namespace->catalog ? false :
! 								tbinfo->dobj.namespace->dobj.dump;
  
  	/*
  	 * In any case, a table can be excluded by an exclusion switch
*************** selectDumpableTable(TableInfo *tbinfo)
*** 1297,1302 ****
--- 1309,1321 ----
  static void
  selectDumpableType(TypeInfo *tyinfo)
  {
+ 	/* Skip types in the catalog. */
+ 	if (tyinfo->dobj.namespace->catalog)
+ 	{
+ 		tyinfo->dobj.dump = false;
+ 		return;
+ 	}
+ 
  	/* skip complex types, except for standalone composite types */
  	if (OidIsValid(tyinfo->typrelid) &&
  		tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
*************** selectDumpableType(TypeInfo *tyinfo)
*** 1347,1353 ****
  static void
  selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
  {
! 	if (dinfo->dobj.namespace)
  		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
  	else
  		dinfo->dobj.dump = dopt->include_everything;
--- 1366,1372 ----
  static void
  selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
  {
! 	if (dinfo->dobj.namespace && !dinfo->dobj.namespace->catalog)
  		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
  	else
  		dinfo->dobj.dump = dopt->include_everything;
*************** selectDumpableObject(DumpableObject *dob
*** 1402,1410 ****
  	/*
  	 * Default policy is to dump if parent namespace is dumpable, or always
  	 * for non-namespace-associated items.
  	 */
! 	if (dobj->namespace)
  		dobj->dump = dobj->namespace->dobj.dump;
  	else
  		dobj->dump = true;
  }
--- 1421,1433 ----
  	/*
  	 * Default policy is to dump if parent namespace is dumpable, or always
  	 * for non-namespace-associated items.
+ 	 *
+ 	 * For the catalog, however, we only consider functions currently.
  	 */
! 	if (dobj->namespace && !dobj->namespace->catalog)
  		dobj->dump = dobj->namespace->dobj.dump;
+ 	else if (dobj->namespace && dobj->namespace->catalog)
+ 		dobj->dump = dobj->objType == DO_FUNC ? true : false;
  	else
  		dobj->dump = true;
  }
*************** getFuncs(Archive *fout, DumpOptions *dop
*** 4364,4374 ****
  
  	/*
  	 * Find all user-defined functions.  Normally we can exclude functions in
! 	 * pg_catalog, which is worth doing since there are several thousand of
! 	 * 'em.  However, there are some extensions that create functions in
! 	 * pg_catalog.  In normal dumps we can still ignore those --- but in
! 	 * binary-upgrade mode, we must dump the member objects of the extension,
! 	 * so be sure to fetch any such functions.
  	 *
  	 * Also, in 9.2 and up, exclude functions that are internally dependent on
  	 * something else, since presumably those will be created as a result of
--- 4387,4397 ----
  
  	/*
  	 * Find all user-defined functions.  Normally we can exclude functions in
! 	 * pg_catalog, provided their ACLs are still the default, which is worth
! 	 * doing since there are several thousand of 'em.  However, there are
! 	 * some extensions that create functions in pg_catalog.  In normal dumps we
! 	 * can still ignore those --- but in binary-upgrade mode, we must dump the
! 	 * member objects of the extension, so be sure to fetch any such functions.
  	 *
  	 * Also, in 9.2 and up, exclude functions that are internally dependent on
  	 * something else, since presumably those will be created as a result of
*************** getFuncs(Archive *fout, DumpOptions *dop
*** 4389,4395 ****
  						  "WHERE NOT proisagg AND ("
  						  "pronamespace != "
  						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog')",
  						  username_subquery);
  		if (fout->remoteVersion >= 90200)
  			appendPQExpBufferStr(query,
--- 4412,4422 ----
  						  "WHERE NOT proisagg AND ("
  						  "pronamespace != "
  						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog') OR "
! 						  "(pronamespace = "
! 						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog') AND "
! 						  "proacl IS NOT NULL)",
  						  username_subquery);
  		if (fout->remoteVersion >= 90200)
  			appendPQExpBufferStr(query,
*************** dumpNamespace(Archive *fout, DumpOptions
*** 8246,8252 ****
  	char	   *qnspname;
  
  	/* Skip if not to be dumped */
! 	if (!nspinfo->dobj.dump || dopt->dataOnly)
  		return;
  
  	/* don't dump dummy namespace from pre-7.3 source */
--- 8273,8279 ----
  	char	   *qnspname;
  
  	/* Skip if not to be dumped */
! 	if (!nspinfo->dobj.dump || dopt->dataOnly || nspinfo->catalog)
  		return;
  
  	/* don't dump dummy namespace from pre-7.3 source */
*************** dumpFunc(Archive *fout, DumpOptions *dop
*** 10393,10412 ****
  	if (dopt->binary_upgrade)
  		binary_upgrade_extension_member(q, &finfo->dobj, labelq->data);
  
! 	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
! 				 funcsig_tag,
! 				 finfo->dobj.namespace->dobj.name,
! 				 NULL,
! 				 finfo->rolname, false,
! 				 "FUNCTION", SECTION_PRE_DATA,
! 				 q->data, delqry->data, NULL,
! 				 NULL, 0,
! 				 NULL, NULL);
  
- 	/* Dump Function Comments and Security Labels */
- 	dumpComment(fout, dopt, labelq->data,
- 				finfo->dobj.namespace->dobj.name, finfo->rolname,
- 				finfo->dobj.catId, 0, finfo->dobj.dumpId);
  	dumpSecLabel(fout, dopt, labelq->data,
  				 finfo->dobj.namespace->dobj.name, finfo->rolname,
  				 finfo->dobj.catId, 0, finfo->dobj.dumpId);
--- 10420,10449 ----
  	if (dopt->binary_upgrade)
  		binary_upgrade_extension_member(q, &finfo->dobj, labelq->data);
  
! 	/*
! 	 * Do not include the definition or comment if its a catalog function,
! 	 * except if it is the member of an extension and this is for a binary
! 	 * upgrade.
! 	 */
! 	if (!finfo->dobj.namespace->catalog ||
! 		(dopt->binary_upgrade && finfo->dobj.ext_member))
! 	{
! 		ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
! 					 funcsig_tag,
! 					 finfo->dobj.namespace->dobj.name,
! 					 NULL,
! 					 finfo->rolname, false,
! 					 "FUNCTION", SECTION_PRE_DATA,
! 					 q->data, delqry->data, NULL,
! 					 NULL, 0,
! 					 NULL, NULL);
! 
! 		/* Dump Function Comments and Security Labels */
! 		dumpComment(fout, dopt, labelq->data,
! 					finfo->dobj.namespace->dobj.name, finfo->rolname,
! 					finfo->dobj.catId, 0, finfo->dobj.dumpId);
! 	}
  
  	dumpSecLabel(fout, dopt, labelq->data,
  				 finfo->dobj.namespace->dobj.name, finfo->rolname,
  				 finfo->dobj.catId, 0, finfo->dobj.dumpId);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index a9d3c10..36100a8
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef struct _namespaceInfo
*** 98,103 ****
--- 98,104 ----
  	DumpableObject dobj;
  	char	   *rolname;		/* name of owner, or empty string */
  	char	   *nspacl;
+ 	bool		catalog;
  } NamespaceInfo;
  
  typedef struct _extensionInfo
#99Stephen Frost
sfrost@snowman.net
In reply to: Stephen Frost (#98)
Re: Additional role attributes && superuser review

All,

* Stephen Frost (sfrost@snowman.net) wrote:

Alright, I've got an initial patch to do this for pg_start/stop_backup,
pg_switch_xlog, and pg_create_restore_point. The actual backend changes
are quite small, as expected. I'll add in the changes for the other
functions being discussed and adapt the documentation changes from
the earlier patch to make sense, but what I'd really appreciate are any
comments or thoughts regarding the changes to pg_dump (which are generic
to all of the function changes, of course).

So, I've tested this approach with extensions and binary upgrade and
things appear to all be working correctly, but there's a few issues
remaining to discuss:

The functions pg_start_backup and pg_stop_backup can currently be run by
replication roles but those roles won't have any permissions on those
functions with the new approach unless we GRANT those rights during
pg_dump and/or pg_upgrade. Note that this isn't an issue for tools
which use the replication protocol (eg: pg_basebackup) since the
replication protocol calls do_pg_start_bacup() directly. As such, my
first question is- do we want to worry about this? We should certainly
mention it in the release notes but I'm not sure if we really want to do
anything else.

The second question is in regards to pg_stat_activity. My first
suggestion for how to address that would be to have the function return
everything and then have the view perform the filtering to return only
what's shown today for users. Admins could then grant access to the
function for whichever users they want to have access to everything.
That strikes me as the best way to avoid changing common usage while
still providing the flexibility. Another option would be to still
invent the MONITOR role attribute and keep the rest the same. Again,
we'd want to mention something in the release notes regarding this
change.

Lastly, there is the question of pg_cancel_backend and
pg_terminate_backend. My thinking on this is to create a new
'pg_signal_backend' which admins could grant access to and leave the
existing functions alone (modulo the change for has_privs_of_role as
discussed previously). We'd rename the current 'pg_signal_backend' to
something else (maybe '_helper'); it's not exposed anywhere and
therefore renaming it shouldn't cause any heartache.

For pg_create_restore_point, pg_switch_xlog, pg_xlog_replay_pause,
pg_xlog_replay_resume, pg_rotate_logfile, we can just remove the
if(!superuser()) ereport() checks and REVOKE rights on them during the
initdb process, since there isn't anything complicated happening for
those today.

One other question which I've been thinking about is if we want to try
and preserve permission changes associated with other catalog objects
(besides functions), or if we even want to change how functions are
handled regarding permission changes. We don't currently preserve any
such changes across dump/restore or even binary upgrades and this would
be changing that. I don't recall any complaints about not preserving
permission changes to objects in pg_catalog, but one could argue that
our lack of doing so today is a bug.

Thoughts?

Thanks!

Stephen

#100Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#99)
Re: Additional role attributes && superuser review

Stephen Frost <sfrost@snowman.net> writes:

... Lastly, there is the question of pg_cancel_backend and
pg_terminate_backend. My thinking on this is to create a new
'pg_signal_backend' which admins could grant access to and leave the
existing functions alone (modulo the change for has_privs_of_role as
discussed previously). We'd rename the current 'pg_signal_backend' to
something else (maybe '_helper'); it's not exposed anywhere and
therefore renaming it shouldn't cause any heartache.

That seems fairly ugly. Why would we need a new, duplicative function
here? (Apologies if the reasoning was spelled out upthread, I've not
been paying much attention.)

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#101Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#100)
Re: Additional role attributes && superuser review

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

... Lastly, there is the question of pg_cancel_backend and
pg_terminate_backend. My thinking on this is to create a new
'pg_signal_backend' which admins could grant access to and leave the
existing functions alone (modulo the change for has_privs_of_role as
discussed previously). We'd rename the current 'pg_signal_backend' to
something else (maybe '_helper'); it's not exposed anywhere and
therefore renaming it shouldn't cause any heartache.

That seems fairly ugly. Why would we need a new, duplicative function
here? (Apologies if the reasoning was spelled out upthread, I've not
been paying much attention.)

Currently, those functions allow users to signal backends which are
owned by them, which means they can be used by anyone. Simply
REVOKE'ing access to them would remove that capability and an admin who
then GRANT's access to the function would need to understand that
they're allowing that user the ability to cancel/terminate any backends
(except those initiated by superusers, at least if we keep that check as
discussed upthread).

If those functions just had simply superuser() checks that prevented
anyone else from using them then this wouldn't be an issue.

REVOKE'ing access *without* removing the permissions checks would defeat
the intent of these changes, which is to allow an administrator to grant
the ability for a certain set of users to cancel and/or terminate
backends started by other users, without also granting those users
superuser rights.

Thanks!

Stephen

#102Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#101)
Re: Additional role attributes && superuser review

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

That seems fairly ugly. Why would we need a new, duplicative function
here? (Apologies if the reasoning was spelled out upthread, I've not
been paying much attention.)

Currently, those functions allow users to signal backends which are
owned by them, which means they can be used by anyone. Simply
REVOKE'ing access to them would remove that capability and an admin who
then GRANT's access to the function would need to understand that
they're allowing that user the ability to cancel/terminate any backends
(except those initiated by superusers, at least if we keep that check as
discussed upthread).

If those functions just had simply superuser() checks that prevented
anyone else from using them then this wouldn't be an issue.

REVOKE'ing access *without* removing the permissions checks would defeat
the intent of these changes, which is to allow an administrator to grant
the ability for a certain set of users to cancel and/or terminate
backends started by other users, without also granting those users
superuser rights.

I see: we have two different use-cases and no way for GRANT/REVOKE
to manage both cases using permissions on a single object. Carry
on then.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#103Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#102)
1 attachment(s)
Re: Additional role attributes && superuser review

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

REVOKE'ing access *without* removing the permissions checks would defeat
the intent of these changes, which is to allow an administrator to grant
the ability for a certain set of users to cancel and/or terminate
backends started by other users, without also granting those users
superuser rights.

I see: we have two different use-cases and no way for GRANT/REVOKE
to manage both cases using permissions on a single object. Carry
on then.

Alright, after going about this three or four different ways, I've
settled on the approach proposed in the attached patch. Most of it is
pretty straight-forward: drop the hard-coded check in the function
itself and REVOKE EXECUTE on the function from PUBLIC. The interesting
bits are those pieces which are more complex than the "all-or-nothing"
cases.

This adds a few new SQL-level functions which return unfiltered results,
if you're allowed to call them based on the GRANT system (and EXECUTE
privileges for them are REVOKE'd from PUBLIC, of course). These are:
pg_stat_get_activity_all, pg_stat_get_wal_senders_all, and
pg_signal_backend (which is similar but not the same- that allows you to
send signals to other backends as a regular user, if you are allowed to
call that function and the other backend wasn't started by a superuser).

There are two new views added, which simply sit on top of the new
functions: pg_stat_activity_all and pg_stat_replication_all.

Minimizing the amount of code duplication wasn't too hard with the
pg_stat_get_wal_senders case; as it was already returning a tuplestore
and so I simply moved most of the logic into a "helper" function which
handled populating the tuplestore and then each call path was relatively
small and mostly boilerplate. pg_stat_get_activity required a bit more
in the way of changes, but they were essentially to modify it to return
a tuplestore like pg_stat_get_wal_senders, and then add in the extra
function and boilerplate for the non-filtered path.

I had considered (and spent a good bit of time implementing...) a number
of alternatives, mostly around trying to do more at the SQL level when
it came to filtering, but that got very ugly very quickly. There's no
simple way to find out "what was the effective role of the caller of
this function" from inside of a security definer function (which would
be required for the filtering, as it would need to be able to call the
function underneath). This is necessary, of course, because functions
in views run as the caller of the view, not as the view owner (that's
useful in some cases, less useful in others). I looked at exposing the
information about the effective role which was calling a function, but
that got pretty ugly too. The GUC system has a stack, but we don't
actually update the 'role' GUC when we are in security definer
functions. There's the notion of an "outer" role ID, but that doesn't
account for intermediate security definer functions and so, while it's
fine for what it does, it wasn't really helpful in this case.

I do still think it'd be nice to provide that information and perhaps we
can do so with fmgr_security_definer(), but it's beyond what's really
needed here and I don't think it'd end up being particularly cleaner to
do it all in SQL now that I've refactored pg_stat_get_activity.

I'd further like to see the ability to declare on either a function call
by function call basis (inside a view defintion), of even at the view
level (as that would allow me to build up multiple views with different
parameters) "who to run this function as", where the options would be
"view owner" or "view querier", very similar to our SECURITY DEFINER vs.
SECURITY INVOKER options for functions today. This is interesting
because, more and more, we're building functions which are actually
returning data sets, not individual values, and we want to filter those
sets sometimes and not other times. In any case, those are really
thoughts for the future and get away from what this is all about, which
is reducing the need for monitoring and/or "regular" admins to have the
"superuser" bit when they don't really need it.

Clearly, further testing and documentation is required and I'll be
getting to that over the next couple of days, but it's pretty darn late
and I'm currently getting libpq undefined reference errors, probably
because I need to set LD_LIBRARY_PATH, but I'm just too tired to now. :)

Thoughts?

Thanks!

Stephen

Attachments:

catalog_function_acls_v2.patchtext/x-diff; charset=us-asciiDownload
diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
new file mode 100644
index 212fd1d..538ebdc
*** a/contrib/test_decoding/expected/permissions.out
--- b/contrib/test_decoding/expected/permissions.out
*************** SET synchronous_commit = on;
*** 4,9 ****
--- 4,16 ----
  CREATE ROLE lr_normal;
  CREATE ROLE lr_superuser SUPERUSER;
  CREATE ROLE lr_replication REPLICATION;
+ GRANT EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_drop_replication_slot(name) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_create_logical_replication_slot(name,name) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
  CREATE TABLE lr_test(data text);
  -- superuser can control replication
  SET ROLE lr_superuser;
*************** RESET ROLE;
*** 54,66 ****
  -- plain user *can't* can control replication
  SET ROLE lr_normal;
  SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
! ERROR:  must be superuser or replication role to use replication slots
  INSERT INTO lr_test VALUES('lr_superuser_init');
  ERROR:  permission denied for relation lr_test
  SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
! ERROR:  must be superuser or replication role to use replication slots
  SELECT pg_drop_replication_slot('regression_slot');
! ERROR:  must be superuser or replication role to use replication slots
  RESET ROLE;
  -- replication users can drop superuser created slots
  SET ROLE lr_superuser;
--- 61,73 ----
  -- plain user *can't* can control replication
  SET ROLE lr_normal;
  SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
! ERROR:  permission denied for function pg_create_logical_replication_slot
  INSERT INTO lr_test VALUES('lr_superuser_init');
  ERROR:  permission denied for relation lr_test
  SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
! ERROR:  permission denied for function pg_logical_slot_get_changes
  SELECT pg_drop_replication_slot('regression_slot');
! ERROR:  permission denied for function pg_drop_replication_slot
  RESET ROLE;
  -- replication users can drop superuser created slots
  SET ROLE lr_superuser;
*************** SELECT 'init' FROM pg_create_logical_rep
*** 90,96 ****
  RESET ROLE;
  SET ROLE lr_normal;
  SELECT pg_drop_replication_slot('regression_slot');
! ERROR:  must be superuser or replication role to use replication slots
  RESET ROLE;
  -- all users can see existing slots
  SET ROLE lr_superuser;
--- 97,103 ----
  RESET ROLE;
  SET ROLE lr_normal;
  SELECT pg_drop_replication_slot('regression_slot');
! ERROR:  permission denied for function pg_drop_replication_slot
  RESET ROLE;
  -- all users can see existing slots
  SET ROLE lr_superuser;
*************** SELECT pg_drop_replication_slot('regress
*** 126,130 ****
--- 133,139 ----
  
  DROP ROLE lr_normal;
  DROP ROLE lr_superuser;
+ SET client_min_messages TO 'warning';
+ DROP OWNED BY lr_replication CASCADE;
  DROP ROLE lr_replication;
  DROP TABLE lr_test;
diff --git a/contrib/test_decoding/sql/permissions.sql b/contrib/test_decoding/sql/permissions.sql
new file mode 100644
index 8680c55..2c02d14
*** a/contrib/test_decoding/sql/permissions.sql
--- b/contrib/test_decoding/sql/permissions.sql
*************** SET synchronous_commit = on;
*** 5,10 ****
--- 5,17 ----
  CREATE ROLE lr_normal;
  CREATE ROLE lr_superuser SUPERUSER;
  CREATE ROLE lr_replication REPLICATION;
+ GRANT EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_drop_replication_slot(name) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_create_logical_replication_slot(name,name) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+ GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
  CREATE TABLE lr_test(data text);
  
  -- superuser can control replication
*************** SELECT pg_drop_replication_slot('regress
*** 65,69 ****
--- 72,78 ----
  
  DROP ROLE lr_normal;
  DROP ROLE lr_superuser;
+ SET client_min_messages TO 'warning';
+ DROP OWNED BY lr_replication CASCADE;
  DROP ROLE lr_replication;
  DROP TABLE lr_test;
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 2179bf7..c9d9f3d
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
*************** pg_start_backup(PG_FUNCTION_ARGS)
*** 54,64 ****
  
  	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);
  
  	PG_RETURN_LSN(startpoint);
--- 54,59 ----
*************** pg_stop_backup(PG_FUNCTION_ARGS)
*** 82,92 ****
  {
  	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);
  
  	PG_RETURN_LSN(stoppoint);
--- 77,82 ----
*************** pg_switch_xlog(PG_FUNCTION_ARGS)
*** 100,110 ****
  {
  	XLogRecPtr	switchpoint;
  
- 	if (!superuser())
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 			 (errmsg("must be superuser to switch transaction log files"))));
- 
  	if (RecoveryInProgress())
  		ereport(ERROR,
  				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
--- 90,95 ----
*************** pg_create_restore_point(PG_FUNCTION_ARGS
*** 129,139 ****
  	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,
  				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
--- 114,119 ----
*************** pg_xlogfile_name(PG_FUNCTION_ARGS)
*** 338,348 ****
  Datum
  pg_xlog_replay_pause(PG_FUNCTION_ARGS)
  {
- 	if (!superuser())
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 				 (errmsg("must be superuser to control recovery"))));
- 
  	if (!RecoveryInProgress())
  		ereport(ERROR,
  				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
--- 318,323 ----
*************** pg_xlog_replay_pause(PG_FUNCTION_ARGS)
*** 360,370 ****
  Datum
  pg_xlog_replay_resume(PG_FUNCTION_ARGS)
  {
- 	if (!superuser())
- 		ereport(ERROR,
- 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- 				 (errmsg("must be superuser to control recovery"))));
- 
  	if (!RecoveryInProgress())
  		ereport(ERROR,
  				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
--- 335,340 ----
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index 2800f73..a6f9674
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_stat_activity AS
*** 623,628 ****
--- 623,652 ----
      WHERE S.datid = D.oid AND
              S.usesysid = U.oid;
  
+ CREATE VIEW pg_stat_activity_all AS
+     SELECT
+             S.datid AS datid,
+             D.datname AS datname,
+             S.pid,
+             S.usesysid,
+             U.rolname AS usename,
+             S.application_name,
+             S.client_addr,
+             S.client_hostname,
+             S.client_port,
+             S.backend_start,
+             S.xact_start,
+             S.query_start,
+             S.state_change,
+             S.waiting,
+             S.state,
+             S.backend_xid,
+             s.backend_xmin,
+             S.query
+     FROM pg_database D, pg_stat_get_activity_all(NULL) AS S, pg_authid U
+     WHERE S.datid = D.oid AND
+             S.usesysid = U.oid;
+ 
  CREATE VIEW pg_stat_replication AS
      SELECT
              S.pid,
*************** CREATE VIEW pg_stat_replication AS
*** 646,651 ****
--- 670,698 ----
      WHERE S.usesysid = U.oid AND
              S.pid = W.pid;
  
+ CREATE VIEW pg_stat_replication_all AS
+     SELECT
+             S.pid,
+             S.usesysid,
+             U.rolname AS usename,
+             S.application_name,
+             S.client_addr,
+             S.client_hostname,
+             S.client_port,
+             S.backend_start,
+             S.backend_xmin,
+             W.state,
+             W.sent_location,
+             W.write_location,
+             W.flush_location,
+             W.replay_location,
+             W.sync_priority,
+             W.sync_state
+     FROM pg_stat_get_activity_all(NULL) AS S, pg_authid U,
+             pg_stat_get_wal_senders_all() AS W
+     WHERE S.usesysid = U.oid AND
+             S.pid = W.pid;
+ 
  CREATE VIEW pg_replication_slots AS
      SELECT
              L.slot_name,
*************** RETURNS interval
*** 897,899 ****
--- 944,967 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'make_interval';
+ 
+ -- Revoke privileges for functions that should not be available to
+ -- all users.  Administrators are allowed to change this later, if
+ -- they wish.
+ REVOKE EXECUTE ON FUNCTION pg_start_backup(text, boolean) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_stop_backup() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_switch_xlog() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_create_restore_point(text) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_rotate_logfile() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_signal_backend(int, int) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_stat_get_activity_all(integer) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_stat_get_wal_senders_all() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_xlog_replay_pause() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_xlog_replay_resume() FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_create_logical_replication_slot(name, name) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_drop_replication_slot(name) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+ REVOKE EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
new file mode 100644
index 3be5263..2995bfa
*** a/src/backend/replication/logical/logicalfuncs.c
--- b/src/backend/replication/logical/logicalfuncs.c
*************** XLogRead(char *buf, TimeLineID tli, XLog
*** 202,216 ****
  	}
  }
  
- 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.
   *
--- 202,207 ----
*************** pg_logical_slot_get_changes_guts(Functio
*** 324,331 ****
  	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
- 	check_permissions();
- 
  	CheckLogicalDecodingRequirements();
  
  	arr = PG_GETARG_ARRAYTYPE_P(3);
--- 315,320 ----
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
new file mode 100644
index f31925d..c879977
*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 23,37 ****
  #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.
--- 23,28 ----
*************** pg_create_physical_replication_slot(PG_F
*** 51,58 ****
  	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
- 	check_permissions();
- 
  	CheckSlotRequirements();
  
  	/* acquire replication slot, this will check for conflicting names */
--- 42,47 ----
*************** pg_create_logical_replication_slot(PG_FU
*** 94,101 ****
  	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  		elog(ERROR, "return type must be a row type");
  
- 	check_permissions();
- 
  	CheckLogicalDecodingRequirements();
  
  	/*
--- 83,88 ----
*************** pg_drop_replication_slot(PG_FUNCTION_ARG
*** 143,150 ****
  {
  	Name		name = PG_GETARG_NAME(0);
  
- 	check_permissions();
- 
  	CheckSlotRequirements();
  
  	ReplicationSlotDrop(NameStr(*name));
--- 130,135 ----
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
new file mode 100644
index da9ee7b..67472db
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
*************** static XLogRecPtr WalSndWaitForWal(XLogR
*** 217,222 ****
--- 217,223 ----
  
  static void XLogRead(char *buf, XLogRecPtr startptr, Size count);
  
+ static void populate_pg_stat_get_wal_senders(TupleDesc tupdesc, Tuplestorestate *tupstore, bool filter);
  
  /* Initialize walsender process before entering the main command loop */
  void
*************** WalSndGetStateString(WalSndState state)
*** 2717,2738 ****
  	return "UNKNOWN";
  }
  
  
  /*
   * Returns activity of walsenders, including pids and xlog locations sent to
!  * standby servers.
   */
  Datum
  pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
  {
- #define PG_STAT_GET_WAL_SENDERS_COLS	8
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
  	TupleDesc	tupdesc;
  	Tuplestorestate *tupstore;
  	MemoryContext per_query_ctx;
  	MemoryContext oldcontext;
- 	WalSnd	   *sync_standby;
- 	int			i;
  
  	/* check to see if caller supports us returning a tuplestore */
  	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
--- 2718,2738 ----
  	return "UNKNOWN";
  }
  
+ #define PG_STAT_GET_WAL_SENDERS_COLS	8
  
  /*
   * Returns activity of walsenders, including pids and xlog locations sent to
!  * standby servers.  Note that this version filters out the results unless the
!  * caller is a superuser.
   */
  Datum
  pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
  {
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
  	TupleDesc	tupdesc;
  	Tuplestorestate *tupstore;
  	MemoryContext per_query_ctx;
  	MemoryContext oldcontext;
  
  	/* check to see if caller supports us returning a tuplestore */
  	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
*************** pg_stat_get_wal_senders(PG_FUNCTION_ARGS
*** 2760,2765 ****
--- 2760,2832 ----
  	MemoryContextSwitchTo(oldcontext);
  
  	/*
+ 	 * Populate the tuplestore.
+ 	 *
+ 	 * For non-superusers, we ask that the results be filtered.
+ 	 */
+ 	populate_pg_stat_get_wal_senders(tupdesc, tupstore, !superuser());
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
+ 
+ /*
+  * Returns activity of walsenders, including pids and xlog locations sent to
+  * standby servers.  Note that this version does NOT filter out the results,
+  * therefore the permissions must be managed at the GRANT level.
+  */
+ Datum
+ pg_stat_get_wal_senders_all(PG_FUNCTION_ARGS)
+ {
+ 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	TupleDesc	tupdesc;
+ 	Tuplestorestate *tupstore;
+ 	MemoryContext per_query_ctx;
+ 	MemoryContext oldcontext;
+ 
+ 	/* check to see if caller supports us returning a tuplestore */
+ 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that cannot accept a set")));
+ 	if (!(rsinfo->allowedModes & SFRM_Materialize))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("materialize mode required, but it is not " \
+ 						"allowed in this context")));
+ 
+ 	/* Build a tuple descriptor for our result type */
+ 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ 		elog(ERROR, "return type must be a row type");
+ 
+ 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+ 	tupstore = tuplestore_begin_heap(true, false, work_mem);
+ 	rsinfo->returnMode = SFRM_Materialize;
+ 	rsinfo->setResult = tupstore;
+ 	rsinfo->setDesc = tupdesc;
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	/* Populate the tuplestore */
+ 	populate_pg_stat_get_wal_senders(tupdesc, tupstore, false);
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
+ 
+ static void
+ populate_pg_stat_get_wal_senders(TupleDesc tupdesc, Tuplestorestate *tupstore, bool filter)
+ {
+ 	WalSnd	   *sync_standby;
+ 	int			i;
+ 
+ 	/*
  	 * Get the currently active synchronous standby.
  	 */
  	LWLockAcquire(SyncRepLock, LW_SHARED);
*************** pg_stat_get_wal_senders(PG_FUNCTION_ARGS
*** 2794,2804 ****
  		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);
  		}
--- 2861,2871 ----
  		memset(nulls, 0, sizeof(nulls));
  		values[0] = Int32GetDatum(walsnd->pid);
  
! 		if (filter)
  		{
  			/*
! 			 * When asked to filter record results, set all the rest of the
! 			 * columns to NULL.
  			 */
  			MemSet(&nulls[1], true, PG_STAT_GET_WAL_SENDERS_COLS - 1);
  		}
*************** pg_stat_get_wal_senders(PG_FUNCTION_ARGS
*** 2843,2852 ****
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
! 	/* clean up and return the tuplestore */
! 	tuplestore_donestoring(tupstore);
! 
! 	return (Datum) 0;
  }
  
  /*
--- 2910,2916 ----
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
! 	return;
  }
  
  /*
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644
index 61d609f..0204da6
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
*************** current_query(PG_FUNCTION_ARGS)
*** 76,86 ****
  }
  
  /*
!  * Send a signal to another backend.
   *
!  * The signal is delivered if the user is either a superuser or the same
!  * role as the backend being signaled. For "dangerous" signals, an explicit
!  * check for superuser needs to be done prior to calling this function.
   *
   * Returns 0 on success, 1 on general failure, 2 on normal permission error
   * and 3 if the caller needs to be a superuser.
--- 76,94 ----
  }
  
  /*
!  * Internal helper function for sending a signal to another backend.
   *
!  * The signal is delivered if the user is a superuser.  If the other backend
!  * is owned by a superuser role, then the calling user must be a superuser.
!  *
!  * When perm_check is passed in as true, then the user must be a member of
!  * the role which owns the backend being signaled.  For "dangerous" signals,
!  * an explicit check for superuser needs to be done prior to calling this
!  * function.
!  *
!  * When perm_check is passwd in as false, then no check of role membership is
!  * performed as the GRANT system is expected to have been used to manage access
!  * to calling the function which called us.
   *
   * Returns 0 on success, 1 on general failure, 2 on normal permission error
   * and 3 if the caller needs to be a superuser.
*************** current_query(PG_FUNCTION_ARGS)
*** 94,100 ****
  #define SIGNAL_BACKEND_NOPERMISSION 2
  #define SIGNAL_BACKEND_NOSUPERUSER 3
  static int
! pg_signal_backend(int pid, int sig)
  {
  	PGPROC	   *proc = BackendPidGetProc(pid);
  
--- 102,108 ----
  #define SIGNAL_BACKEND_NOPERMISSION 2
  #define SIGNAL_BACKEND_NOSUPERUSER 3
  static int
! pg_signal_backend_helper(int pid, int sig, bool perm_check)
  {
  	PGPROC	   *proc = BackendPidGetProc(pid);
  
*************** pg_signal_backend(int pid, int sig)
*** 122,128 ****
  		return SIGNAL_BACKEND_NOSUPERUSER;
  
  	/* Users can signal backends they have role membership in. */
! 	if (!has_privs_of_role(GetUserId(), proc->roleId))
  		return SIGNAL_BACKEND_NOPERMISSION;
  
  	/*
--- 130,136 ----
  		return SIGNAL_BACKEND_NOSUPERUSER;
  
  	/* Users can signal backends they have role membership in. */
! 	if (perm_check && !has_privs_of_role(GetUserId(), proc->roleId))
  		return SIGNAL_BACKEND_NOPERMISSION;
  
  	/*
*************** pg_signal_backend(int pid, int sig)
*** 150,155 ****
--- 158,198 ----
  }
  
  /*
+  * Signal a backend process.  Permissions for this are managed by the GRANT
+  * system and therefore we do not do any extra permissions checks through
+  * this path.
+  *
+  * Note that only superusers can signal superuser-owned processes.
+  */
+ Datum
+ pg_signal_backend(PG_FUNCTION_ARGS)
+ {
+ 	int			backend = PG_GETARG_INT32(0);
+ 	int			signal = PG_GETARG_INT32(1);
+ 	int			r;
+ 
+ 	/*
+ 	 * We only allow "safe" signals to be used through this, unless the user
+ 	 * is a superuser.
+ 	 */
+ 	if (!superuser() && signal != SIGINT && signal != SIGTERM)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be a superuser to send signals other than SIGINT and SIGTERM"))));
+ 
+ 	r = pg_signal_backend_helper(backend, signal, false);
+ 
+ 	if (r == SIGNAL_BACKEND_NOSUPERUSER)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be a superuser to cancel superuser query"))));
+ 
+ 	Assert (r != SIGNAL_BACKEND_NOPERMISSION);
+ 
+ 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+ }
+ 
+ /*
   * Signal to cancel a backend process.  This is allowed if you are a member of
   * the role whose process is being canceled.
   *
*************** pg_signal_backend(int pid, int sig)
*** 158,164 ****
  Datum
  pg_cancel_backend(PG_FUNCTION_ARGS)
  {
! 	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
  
  	if (r == SIGNAL_BACKEND_NOSUPERUSER)
  		ereport(ERROR,
--- 201,207 ----
  Datum
  pg_cancel_backend(PG_FUNCTION_ARGS)
  {
! 	int			r = pg_signal_backend_helper(PG_GETARG_INT32(0), SIGINT, true);
  
  	if (r == SIGNAL_BACKEND_NOSUPERUSER)
  		ereport(ERROR,
*************** pg_cancel_backend(PG_FUNCTION_ARGS)
*** 182,188 ****
  Datum
  pg_terminate_backend(PG_FUNCTION_ARGS)
  {
! 	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
  
  	if (r == SIGNAL_BACKEND_NOSUPERUSER)
  		ereport(ERROR,
--- 225,231 ----
  Datum
  pg_terminate_backend(PG_FUNCTION_ARGS)
  {
! 	int			r = pg_signal_backend_helper(PG_GETARG_INT32(0), SIGTERM, true);
  
  	if (r == SIGNAL_BACKEND_NOSUPERUSER)
  		ereport(ERROR,
*************** pg_reload_conf(PG_FUNCTION_ARGS)
*** 225,235 ****
  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)
  	{
  		ereport(WARNING,
--- 268,273 ----
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
new file mode 100644
index 78adb2d..fa27386
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
*************** extern Datum pg_stat_get_function_self_t
*** 53,58 ****
--- 53,59 ----
  
  extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
  extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
+ extern Datum pg_stat_get_activity_all(PG_FUNCTION_ARGS);
  extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
  extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
  extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
*************** extern Datum pg_stat_reset_single_functi
*** 126,131 ****
--- 127,134 ----
  /* Global bgwriter statistics, from bgwriter.c */
  extern PgStat_MsgBgWriter bgwriterStats;
  
+ static void populate_pg_stat_get_activity(TupleDesc tupdesc, Tuplestorestate *tupstore, int pid, Oid calling_user);
+ 
  Datum
  pg_stat_get_numscans(PG_FUNCTION_ARGS)
  {
*************** pg_stat_get_backend_idset(PG_FUNCTION_AR
*** 524,648 ****
  	}
  }
  
  Datum
  pg_stat_get_activity(PG_FUNCTION_ARGS)
  {
! 	FuncCallContext *funcctx;
  
! 	if (SRF_IS_FIRSTCALL())
! 	{
! 		MemoryContext oldcontext;
! 		TupleDesc	tupdesc;
  
! 		funcctx = SRF_FIRSTCALL_INIT();
  
! 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
! 		tupdesc = CreateTemplateTupleDesc(16, false);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
! 						   OIDOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
! 						   INT4OID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid",
! 						   OIDOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name",
! 						   TEXTOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "state",
! 						   TEXTOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "query",
! 						   TEXTOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "waiting",
! 						   BOOLOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "act_start",
! 						   TIMESTAMPTZOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "query_start",
! 						   TIMESTAMPTZOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "backend_start",
! 						   TIMESTAMPTZOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "state_change",
! 						   TIMESTAMPTZOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_addr",
! 						   INETOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 13, "client_hostname",
! 						   TEXTOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
! 						   INT4OID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 15, "backend_xid",
! 						   XIDOID, -1, 0);
! 		TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
! 						   XIDOID, -1, 0);
  
! 		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
  
! 		funcctx->user_fctx = palloc0(sizeof(int));
! 		if (PG_ARGISNULL(0))
! 		{
! 			/* Get all backends */
! 			funcctx->max_calls = pgstat_fetch_stat_numbackends();
! 		}
! 		else
! 		{
! 			/*
! 			 * Get one backend - locate by pid.
! 			 *
! 			 * We lookup the backend early, so we can return zero rows if it
! 			 * doesn't exist, instead of returning a single row full of NULLs.
! 			 */
! 			int			pid = PG_GETARG_INT32(0);
! 			int			i;
! 			int			n = pgstat_fetch_stat_numbackends();
  
! 			for (i = 1; i <= n; i++)
! 			{
! 				PgBackendStatus *be = pgstat_fetch_stat_beentry(i);
  
! 				if (be)
! 				{
! 					if (be->st_procpid == pid)
! 					{
! 						*(int *) (funcctx->user_fctx) = i;
! 						break;
! 					}
! 				}
! 			}
  
! 			if (*(int *) (funcctx->user_fctx) == 0)
! 				/* Pid not found, return zero rows */
! 				funcctx->max_calls = 0;
! 			else
! 				funcctx->max_calls = 1;
! 		}
  
! 		MemoryContextSwitchTo(oldcontext);
! 	}
  
! 	/* stuff done on every call of the function */
! 	funcctx = SRF_PERCALL_SETUP();
  
! 	if (funcctx->call_cntr < funcctx->max_calls)
  	{
  		/* for each row */
  		Datum		values[16];
  		bool		nulls[16];
- 		HeapTuple	tuple;
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
  
  		MemSet(values, 0, sizeof(values));
  		MemSet(nulls, 0, sizeof(nulls));
  
! 		if (*(int *) (funcctx->user_fctx) > 0)
! 		{
! 			/* Get specific pid slot */
! 			local_beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx));
! 			beentry = &local_beentry->backendStatus;
! 		}
! 		else
  		{
! 			/* Get the next one in the list */
! 			local_beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1);	/* 1-based index */
! 			beentry = &local_beentry->backendStatus;
  		}
  		if (!beentry)
  		{
  			int			i;
--- 527,672 ----
  	}
  }
  
+ /*
+  * Returns activity of other backends.  Note that this version filters out
+  * the results unless the caller is a member of the role of the backend being
+  * examined.
+  */
  Datum
  pg_stat_get_activity(PG_FUNCTION_ARGS)
  {
! 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
! 	TupleDesc   tupdesc;
! 	Tuplestorestate *tupstore;
! 	MemoryContext per_query_ctx;
! 	MemoryContext oldcontext;
  
! 	/* check to see if caller supports us returning a tuplestore */
! 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("set-valued function called in context that cannot accept a set")));
! 	if (!(rsinfo->allowedModes & SFRM_Materialize))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("materialize mode required, but it is not " \
! 						"allowed in this context")));
  
! 	/* Build a tuple descriptor for our result type */
! 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
! 		elog(ERROR, "return type must be a row type");
  
! 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
! 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
  
! 	tupstore = tuplestore_begin_heap(true, false, work_mem);
! 	rsinfo->returnMode = SFRM_Materialize;
! 	rsinfo->setResult = tupstore;
! 	rsinfo->setDesc = tupdesc;
  
! 	MemoryContextSwitchTo(oldcontext);
  
! 	/*
! 	 * Populate the tuplestore.
! 	 *
! 	 * For this path, we pass in the current GetUserId() result and have the
! 	 * populate function filter the results based on that.
! 	 */
! 	populate_pg_stat_get_activity(tupdesc, tupstore, PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0), GetUserId());
  
! 	/* clean up and return the tuplestore */
! 	tuplestore_donestoring(tupstore);
  
! 	return (Datum) 0;
! }
  
! /*
!  * Returns activity of other backends.  Note that this version does NOT
!  * filter the results and therefore the permissions at the SQL level must
!  * be REVOKE'd from public.
!  */
! Datum
! pg_stat_get_activity_all(PG_FUNCTION_ARGS)
! {
! 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
! 	TupleDesc   tupdesc;
! 	Tuplestorestate *tupstore;
! 	MemoryContext per_query_ctx;
! 	MemoryContext oldcontext;
  
! 	/* check to see if caller supports us returning a tuplestore */
! 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("set-valued function called in context that cannot accept a set")));
! 	if (!(rsinfo->allowedModes & SFRM_Materialize))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("materialize mode required, but it is not " \
! 						"allowed in this context")));
  
! 	/* Build a tuple descriptor for our result type */
! 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
! 		elog(ERROR, "return type must be a row type");
  
! 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
! 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
! 
! 	tupstore = tuplestore_begin_heap(true, false, work_mem);
! 	rsinfo->returnMode = SFRM_Materialize;
! 	rsinfo->setResult = tupstore;
! 	rsinfo->setDesc = tupdesc;
! 
! 	MemoryContextSwitchTo(oldcontext);
! 
! 	/*
! 	 * Populate the tuplestore.
! 	 *
! 	 * For this path, we pass in the current GetUserId() result and have the
! 	 * populate function filter the results based on that.
! 	 */
! 	populate_pg_stat_get_activity(tupdesc, tupstore, PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0), InvalidOid);
! 
! 	/* clean up and return the tuplestore */
! 	tuplestore_donestoring(tupstore);
! 
! 	return (Datum) 0;
! }
! 
! static void
! populate_pg_stat_get_activity(TupleDesc tupdesc, Tuplestorestate *tupstore, int pid, Oid calling_user)
! {
! 	int num_backends = pgstat_fetch_stat_numbackends();
! 	int curr_backend;
! 
! 	/* 1-based index */
! 	for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
  	{
  		/* for each row */
  		Datum		values[16];
  		bool		nulls[16];
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
  
  		MemSet(values, 0, sizeof(values));
  		MemSet(nulls, 0, sizeof(nulls));
  
! 		if (pid != -1)
  		{
! 			/* Skip any which are not the one we're looking for. */
! 			PgBackendStatus *be = pgstat_fetch_stat_beentry(curr_backend);
! 
! 			if (!be || be->st_procpid != pid)
! 				continue;
! 
  		}
+ 
+ 		/* Get the next one in the list */
+ 		local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+ 		if (!local_beentry)
+ 			continue;
+ 
+ 		beentry = &local_beentry->backendStatus;
  		if (!beentry)
  		{
  			int			i;
*************** pg_stat_get_activity(PG_FUNCTION_ARGS)
*** 653,660 ****
  			nulls[5] = false;
  			values[5] = CStringGetTextDatum("<backend information not available>");
  
! 			tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
! 			SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
  		}
  
  		/* Values available to all callers */
--- 677,684 ----
  			nulls[5] = false;
  			values[5] = CStringGetTextDatum("<backend information not available>");
  
! 			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
! 			continue;
  		}
  
  		/* Values available to all callers */
*************** pg_stat_get_activity(PG_FUNCTION_ARGS)
*** 677,683 ****
  			nulls[15] = true;
  
  		/* Values only available to role member */
! 		if (has_privs_of_role(GetUserId(), beentry->st_userid))
  		{
  			SockAddr	zero_clientaddr;
  
--- 701,708 ----
  			nulls[15] = true;
  
  		/* Values only available to role member */
! 		if (calling_user == InvalidOid ||
! 			has_privs_of_role(calling_user, beentry->st_userid))
  		{
  			SockAddr	zero_clientaddr;
  
*************** pg_stat_get_activity(PG_FUNCTION_ARGS)
*** 812,826 ****
  			nulls[13] = true;
  		}
  
! 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
  
! 		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
! 	}
! 	else
! 	{
! 		/* nothing left */
! 		SRF_RETURN_DONE(funcctx);
  	}
  }
  
  
--- 837,850 ----
  			nulls[13] = true;
  		}
  
! 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  
! 		/* If only a single backend was requested, and we found it, break. */
! 		if (pid != -1)
! 			break;
  	}
+ 
+ 	return;
  }
  
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 7da5c41..c20f6c3
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** selectDumpableNamespace(NamespaceInfo *n
*** 1234,1245 ****
--- 1234,1255 ----
  	 * If specific tables are being dumped, do not dump any complete
  	 * namespaces. If specific namespaces are being dumped, dump just those
  	 * namespaces. Otherwise, dump all non-system namespaces.
+ 	 *
+ 	 * Note that we do consider dumping ACLs of functions in pg_catalog,
+ 	 * so mark that as a dumpable namespace, but further mark it as the
+ 	 * catalog namespace.
  	 */
+ 
+ 	/* Will be set when we may be dumping catalog ACLs, see below. */
+ 	nsinfo->catalog = false;
+ 
  	if (table_include_oids.head != NULL)
  		nsinfo->dobj.dump = false;
  	else if (schema_include_oids.head != NULL)
  		nsinfo->dobj.dump = simple_oid_list_member(&schema_include_oids,
  												   nsinfo->dobj.catId.oid);
+ 	else if (strncmp(nsinfo->dobj.name, "pg_catalog", 10) == 0)
+ 		nsinfo->dobj.dump = nsinfo->catalog = true;
  	else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
  			 strcmp(nsinfo->dobj.name, "information_schema") == 0)
  		nsinfo->dobj.dump = false;
*************** selectDumpableTable(TableInfo *tbinfo)
*** 1264,1276 ****
  {
  	/*
  	 * If specific tables are being dumped, dump just those tables; else, dump
! 	 * according to the parent namespace's dump flag.
  	 */
  	if (table_include_oids.head != NULL)
  		tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
  												   tbinfo->dobj.catId.oid);
  	else
! 		tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump;
  
  	/*
  	 * In any case, a table can be excluded by an exclusion switch
--- 1274,1288 ----
  {
  	/*
  	 * If specific tables are being dumped, dump just those tables; else, dump
! 	 * according to the parent namespace's dump flag, except we never dump
! 	 * catalog tables.
  	 */
  	if (table_include_oids.head != NULL)
  		tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
  												   tbinfo->dobj.catId.oid);
  	else
! 		tbinfo->dobj.dump = tbinfo->dobj.namespace->catalog ? false :
! 								tbinfo->dobj.namespace->dobj.dump;
  
  	/*
  	 * In any case, a table can be excluded by an exclusion switch
*************** selectDumpableTable(TableInfo *tbinfo)
*** 1297,1302 ****
--- 1309,1321 ----
  static void
  selectDumpableType(TypeInfo *tyinfo)
  {
+ 	/* Skip types in the catalog. */
+ 	if (tyinfo->dobj.namespace->catalog)
+ 	{
+ 		tyinfo->dobj.dump = false;
+ 		return;
+ 	}
+ 
  	/* skip complex types, except for standalone composite types */
  	if (OidIsValid(tyinfo->typrelid) &&
  		tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
*************** selectDumpableType(TypeInfo *tyinfo)
*** 1347,1353 ****
  static void
  selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
  {
! 	if (dinfo->dobj.namespace)
  		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
  	else
  		dinfo->dobj.dump = dopt->include_everything;
--- 1366,1372 ----
  static void
  selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
  {
! 	if (dinfo->dobj.namespace && !dinfo->dobj.namespace->catalog)
  		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
  	else
  		dinfo->dobj.dump = dopt->include_everything;
*************** selectDumpableObject(DumpableObject *dob
*** 1402,1410 ****
  	/*
  	 * Default policy is to dump if parent namespace is dumpable, or always
  	 * for non-namespace-associated items.
  	 */
! 	if (dobj->namespace)
  		dobj->dump = dobj->namespace->dobj.dump;
  	else
  		dobj->dump = true;
  }
--- 1421,1433 ----
  	/*
  	 * Default policy is to dump if parent namespace is dumpable, or always
  	 * for non-namespace-associated items.
+ 	 *
+ 	 * For the catalog, however, we only consider functions currently.
  	 */
! 	if (dobj->namespace && !dobj->namespace->catalog)
  		dobj->dump = dobj->namespace->dobj.dump;
+ 	else if (dobj->namespace && dobj->namespace->catalog)
+ 		dobj->dump = dobj->objType == DO_FUNC ? true : false;
  	else
  		dobj->dump = true;
  }
*************** getFuncs(Archive *fout, DumpOptions *dop
*** 4364,4374 ****
  
  	/*
  	 * Find all user-defined functions.  Normally we can exclude functions in
! 	 * pg_catalog, which is worth doing since there are several thousand of
! 	 * 'em.  However, there are some extensions that create functions in
! 	 * pg_catalog.  In normal dumps we can still ignore those --- but in
! 	 * binary-upgrade mode, we must dump the member objects of the extension,
! 	 * so be sure to fetch any such functions.
  	 *
  	 * Also, in 9.2 and up, exclude functions that are internally dependent on
  	 * something else, since presumably those will be created as a result of
--- 4387,4397 ----
  
  	/*
  	 * Find all user-defined functions.  Normally we can exclude functions in
! 	 * pg_catalog, provided their ACLs are still the default, which is worth
! 	 * doing since there are several thousand of 'em.  However, there are
! 	 * some extensions that create functions in pg_catalog.  In normal dumps we
! 	 * can still ignore those --- but in binary-upgrade mode, we must dump the
! 	 * member objects of the extension, so be sure to fetch any such functions.
  	 *
  	 * Also, in 9.2 and up, exclude functions that are internally dependent on
  	 * something else, since presumably those will be created as a result of
*************** getFuncs(Archive *fout, DumpOptions *dop
*** 4387,4395 ****
  						  "(%s proowner) AS rolname "
  						  "FROM pg_proc p "
  						  "WHERE NOT proisagg AND ("
! 						  "pronamespace != "
  						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog')",
  						  username_subquery);
  		if (fout->remoteVersion >= 90200)
  			appendPQExpBufferStr(query,
--- 4410,4422 ----
  						  "(%s proowner) AS rolname "
  						  "FROM pg_proc p "
  						  "WHERE NOT proisagg AND ("
! 						  "(pronamespace != "
  						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog') OR "
! 						  "(pronamespace = "
! 						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog') AND "
! 						  "proacl IS NOT NULL))",
  						  username_subquery);
  		if (fout->remoteVersion >= 90200)
  			appendPQExpBufferStr(query,
*************** dumpNamespace(Archive *fout, DumpOptions
*** 8246,8252 ****
  	char	   *qnspname;
  
  	/* Skip if not to be dumped */
! 	if (!nspinfo->dobj.dump || dopt->dataOnly)
  		return;
  
  	/* don't dump dummy namespace from pre-7.3 source */
--- 8273,8279 ----
  	char	   *qnspname;
  
  	/* Skip if not to be dumped */
! 	if (!nspinfo->dobj.dump || dopt->dataOnly || nspinfo->catalog)
  		return;
  
  	/* don't dump dummy namespace from pre-7.3 source */
*************** dumpFunc(Archive *fout, DumpOptions *dop
*** 10393,10412 ****
  	if (dopt->binary_upgrade)
  		binary_upgrade_extension_member(q, &finfo->dobj, labelq->data);
  
! 	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
! 				 funcsig_tag,
! 				 finfo->dobj.namespace->dobj.name,
! 				 NULL,
! 				 finfo->rolname, false,
! 				 "FUNCTION", SECTION_PRE_DATA,
! 				 q->data, delqry->data, NULL,
! 				 NULL, 0,
! 				 NULL, NULL);
  
- 	/* Dump Function Comments and Security Labels */
- 	dumpComment(fout, dopt, labelq->data,
- 				finfo->dobj.namespace->dobj.name, finfo->rolname,
- 				finfo->dobj.catId, 0, finfo->dobj.dumpId);
  	dumpSecLabel(fout, dopt, labelq->data,
  				 finfo->dobj.namespace->dobj.name, finfo->rolname,
  				 finfo->dobj.catId, 0, finfo->dobj.dumpId);
--- 10420,10449 ----
  	if (dopt->binary_upgrade)
  		binary_upgrade_extension_member(q, &finfo->dobj, labelq->data);
  
! 	/*
! 	 * Do not include the definition or comment if its a catalog function,
! 	 * except if it is the member of an extension and this is for a binary
! 	 * upgrade.
! 	 */
! 	if (!finfo->dobj.namespace->catalog ||
! 		(dopt->binary_upgrade && finfo->dobj.ext_member))
! 	{
! 		ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
! 					 funcsig_tag,
! 					 finfo->dobj.namespace->dobj.name,
! 					 NULL,
! 					 finfo->rolname, false,
! 					 "FUNCTION", SECTION_PRE_DATA,
! 					 q->data, delqry->data, NULL,
! 					 NULL, 0,
! 					 NULL, NULL);
! 
! 		/* Dump Function Comments and Security Labels */
! 		dumpComment(fout, dopt, labelq->data,
! 					finfo->dobj.namespace->dobj.name, finfo->rolname,
! 					finfo->dobj.catId, 0, finfo->dobj.dumpId);
! 	}
  
  	dumpSecLabel(fout, dopt, labelq->data,
  				 finfo->dobj.namespace->dobj.name, finfo->rolname,
  				 finfo->dobj.catId, 0, finfo->dobj.dumpId);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index a9d3c10..36100a8
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef struct _namespaceInfo
*** 98,103 ****
--- 98,104 ----
  	DumpableObject dobj;
  	char	   *rolname;		/* name of owner, or empty string */
  	char	   *nspacl;
+ 	bool		catalog;
  } NamespaceInfo;
  
  typedef struct _extensionInfo
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 8890ade..abe4e2c
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 1936 (  pg_stat_get_ba
*** 2756,2763 ****
--- 2756,2767 ----
  DESCR("statistics: currently active backend IDs");
  DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active backends");
+ DATA(insert OID = 3283 (  pg_stat_get_activity_all			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity_all _null_ _null_ _null_ ));
+ DESCR("statistics: information about currently active backends, unfiltered");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
+ DATA(insert OID = 3285 (  pg_stat_get_wal_senders_all	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders_all _null_ _null_ _null_ ));
+ DESCR("statistics: information about currently active replication, unfiltered");
  DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
  DESCR("statistics: current backend PID");
  DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
*************** DATA(insert OID = 2171 ( pg_cancel_backe
*** 3107,3112 ****
--- 3111,3119 ----
  DESCR("cancel a server process' current query");
  DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
  DESCR("terminate a server process");
+ DATA(insert OID = 3284 ( pg_signal_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "23 23" _null_ _null_ _null_ _null_ pg_signal_backend _null_ _null_ _null_ ));
+ DESCR("signal a server process");
+ 
  DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 3220 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
  DESCR("prepare for taking an online backup");
  DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
new file mode 100644
index b10e784..e458621
*** a/src/include/replication/walsender.h
--- b/src/include/replication/walsender.h
*************** extern void WalSndWakeup(void);
*** 37,42 ****
--- 37,43 ----
  extern void WalSndRqstFileReload(void);
  
  extern Datum pg_stat_get_wal_senders(PG_FUNCTION_ARGS);
+ extern Datum pg_stat_get_wal_senders_all(PG_FUNCTION_ARGS);
  
  /*
   * Remember that we want to wakeup walsenders later
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index 6310641..444113a
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_ls_dir(PG_FUNCTION_ARGS)
*** 482,487 ****
--- 482,488 ----
  extern Datum current_database(PG_FUNCTION_ARGS);
  extern Datum current_query(PG_FUNCTION_ARGS);
  extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+ extern Datum pg_signal_backend(PG_FUNCTION_ARGS);
  extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
  extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
  extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
new file mode 100644
index 1788270..b9ac3b8
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_stat_activity| SELECT s.datid,
*** 1636,1641 ****
--- 1636,1663 ----
      pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
      pg_authid u
    WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
+ pg_stat_activity_all| SELECT s.datid,
+     d.datname,
+     s.pid,
+     s.usesysid,
+     u.rolname AS usename,
+     s.application_name,
+     s.client_addr,
+     s.client_hostname,
+     s.client_port,
+     s.backend_start,
+     s.xact_start,
+     s.query_start,
+     s.state_change,
+     s.waiting,
+     s.state,
+     s.backend_xid,
+     s.backend_xmin,
+     s.query
+    FROM pg_database d,
+     pg_stat_get_activity_all(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
+     pg_authid u
+   WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
  pg_stat_all_indexes| SELECT c.oid AS relid,
      i.oid AS indexrelid,
      n.nspname AS schemaname,
*************** pg_stat_replication| SELECT s.pid,
*** 1743,1748 ****
--- 1765,1790 ----
      pg_authid u,
      pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
    WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+ pg_stat_replication_all| SELECT s.pid,
+     s.usesysid,
+     u.rolname AS usename,
+     s.application_name,
+     s.client_addr,
+     s.client_hostname,
+     s.client_port,
+     s.backend_start,
+     s.backend_xmin,
+     w.state,
+     w.sent_location,
+     w.write_location,
+     w.flush_location,
+     w.replay_location,
+     w.sync_priority,
+     w.sync_state
+    FROM pg_stat_get_activity_all(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
+     pg_authid u,
+     pg_stat_get_wal_senders_all() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
+   WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
  pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
      pg_stat_all_indexes.indexrelid,
      pg_stat_all_indexes.schemaname,
#104Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#103)
Re: Additional role attributes && superuser review

On Thu, Apr 2, 2015 at 12:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

REVOKE'ing access *without* removing the permissions checks would defeat
the intent of these changes, which is to allow an administrator to grant
the ability for a certain set of users to cancel and/or terminate
backends started by other users, without also granting those users
superuser rights.

I see: we have two different use-cases and no way for GRANT/REVOKE
to manage both cases using permissions on a single object. Carry
on then.

Alright, after going about this three or four different ways, I've
settled on the approach proposed in the attached patch. Most of it is
pretty straight-forward: drop the hard-coded check in the function
itself and REVOKE EXECUTE on the function from PUBLIC. The interesting
bits are those pieces which are more complex than the "all-or-nothing"
cases.

This adds a few new SQL-level functions which return unfiltered results,
if you're allowed to call them based on the GRANT system (and EXECUTE
privileges for them are REVOKE'd from PUBLIC, of course). These are:
pg_stat_get_activity_all, pg_stat_get_wal_senders_all, and
pg_signal_backend (which is similar but not the same- that allows you to
send signals to other backends as a regular user, if you are allowed to
call that function and the other backend wasn't started by a superuser).

There are two new views added, which simply sit on top of the new
functions: pg_stat_activity_all and pg_stat_replication_all.

Minimizing the amount of code duplication wasn't too hard with the
pg_stat_get_wal_senders case; as it was already returning a tuplestore
and so I simply moved most of the logic into a "helper" function which
handled populating the tuplestore and then each call path was relatively
small and mostly boilerplate. pg_stat_get_activity required a bit more
in the way of changes, but they were essentially to modify it to return
a tuplestore like pg_stat_get_wal_senders, and then add in the extra
function and boilerplate for the non-filtered path.

I had considered (and spent a good bit of time implementing...) a number
of alternatives, mostly around trying to do more at the SQL level when
it came to filtering, but that got very ugly very quickly. There's no
simple way to find out "what was the effective role of the caller of
this function" from inside of a security definer function (which would
be required for the filtering, as it would need to be able to call the
function underneath). This is necessary, of course, because functions
in views run as the caller of the view, not as the view owner (that's
useful in some cases, less useful in others). I looked at exposing the
information about the effective role which was calling a function, but
that got pretty ugly too. The GUC system has a stack, but we don't
actually update the 'role' GUC when we are in security definer
functions. There's the notion of an "outer" role ID, but that doesn't
account for intermediate security definer functions and so, while it's
fine for what it does, it wasn't really helpful in this case.

I do still think it'd be nice to provide that information and perhaps we
can do so with fmgr_security_definer(), but it's beyond what's really
needed here and I don't think it'd end up being particularly cleaner to
do it all in SQL now that I've refactored pg_stat_get_activity.

I'd further like to see the ability to declare on either a function call
by function call basis (inside a view defintion), of even at the view
level (as that would allow me to build up multiple views with different
parameters) "who to run this function as", where the options would be
"view owner" or "view querier", very similar to our SECURITY DEFINER vs.
SECURITY INVOKER options for functions today. This is interesting
because, more and more, we're building functions which are actually
returning data sets, not individual values, and we want to filter those
sets sometimes and not other times. In any case, those are really
thoughts for the future and get away from what this is all about, which
is reducing the need for monitoring and/or "regular" admins to have the
"superuser" bit when they don't really need it.

Clearly, further testing and documentation is required and I'll be
getting to that over the next couple of days, but it's pretty darn late
and I'm currently getting libpq undefined reference errors, probably
because I need to set LD_LIBRARY_PATH, but I'm just too tired to now. :)

Thoughts?

The tricky part of this seems to me to be the pg_dump changes. The
new catalog flag seems a little sketchy to me; wouldn't it be better
to make the dump flag into an enum, DUMP_EVERYTHING, DUMP_ACL,
DUMP_NONE?

Is this creating a user-visible API break, where pg_stat_activity and
pg_stat_replication now always show only the publicly-visible
information, and privileged users must explicitly decide to query the
_all versions? If so, -1 from me on that part of this.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#105Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#104)
Re: Additional role attributes && superuser review

Robert,

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Apr 2, 2015 at 12:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

Clearly, further testing and documentation is required and I'll be
getting to that over the next couple of days, but it's pretty darn late
and I'm currently getting libpq undefined reference errors, probably
because I need to set LD_LIBRARY_PATH, but I'm just too tired to now. :)

Thoughts?

The tricky part of this seems to me to be the pg_dump changes. The
new catalog flag seems a little sketchy to me; wouldn't it be better
to make the dump flag into an enum, DUMP_EVERYTHING, DUMP_ACL,
DUMP_NONE?

I agree that the pg_dump changes are a very big part of this change.
I'll look at using an enum and see if that would work.

Is this creating a user-visible API break, where pg_stat_activity and
pg_stat_replication now always show only the publicly-visible
information, and privileged users must explicitly decide to query the
_all versions? If so, -1 from me on that part of this.

Thanks for bringing it up. No, the existing API is exactly the same for
the existing views- the only difference is that now there are _all
versions which provide unfiltered data (but you have to have permission
to call the _all() functions underneath, or you get a permission denied
error).

Where this does have an API change is with the simpler functions that
used to do "if (superuser() || replication_role())" and now depend on
the GRANT system instead. We can't (today, at least) say:

GRANT EXECUTE ON function_whatever() TO replication_roles;

And have that be kept current as that role attribute is modified. I've
not done it yet, but we could certainly have pg_dump dump out GRANTs for
the *current* users which have that role attribute and give those users
access to the functions via the normal permissions system. I'm not
really sure that's a good idea though- it might be better to have a
clean break here (and one which is clearly in the "more secure/explicit"
direction) and tell admins in the release notes to GRANT EXECUTE on the
functions for the roles they want to give permission to. We could also
duplicate the functions and have the existing ones remain as-is and have
the new ones just depend on the GRANT system, but I don't particularly
like that since I'm afraid we'd end up in the same boat we're in now
with pg_user and friends.

Thanks!

Stephen

#106Stephen Frost
sfrost@snowman.net
In reply to: Stephen Frost (#105)
1 attachment(s)
Re: Additional role attributes && superuser review

Robert,

* Stephen Frost (sfrost@snowman.net) wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Apr 2, 2015 at 12:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

Clearly, further testing and documentation is required and I'll be
getting to that over the next couple of days, but it's pretty darn late
and I'm currently getting libpq undefined reference errors, probably
because I need to set LD_LIBRARY_PATH, but I'm just too tired to now. :)

Thoughts?

The tricky part of this seems to me to be the pg_dump changes. The
new catalog flag seems a little sketchy to me; wouldn't it be better
to make the dump flag into an enum, DUMP_EVERYTHING, DUMP_ACL,
DUMP_NONE?

I agree that the pg_dump changes are a very big part of this change.
I'll look at using an enum and see if that would work.

I've implemented this approach and there are things which I like about
it and things which I don't. I'd love to hear your thoughts. As
mentioned previously, this patch does not break the pg_stat_activity or
pg_stat_replication view APIs. Similairly, the functions which directly
feed those views return the same results they did previously, there are
just additional functions now which provide everything, and view on top
of those, for users who are GRANT'd access to them.

This does change the API of a few functions which previously allowed
roles with the "replication" attribute to call them. We could provide
additional functions to provide both paths but I don't believe it's
really necessary. Indeed, I feel it's better for administrators to
explicit grant access to those functions instead.

Note that this doesn't use an enum but a bit field for which components
of a given object should be dumped out. While I like that in general,
it meant a lot of changes and I'll be the first to admit that I've not
tested every possible pg_dump option permutation to make sure that the
correct results are returned. I plan to do that in the coming weeks and
will address any issues found.

Is this, more-or-less, what you were thinking of? I looked at removing
the relatively grotty options (dataOnly, aclsSkip, etc) and it didn't
appear trivial to use the bitmask instead. I can look into that further
though, as I do feel that it'd be good if we could reduce our dependence
on those options in favor of the bitmask approach.

Thoughts?

Thanks!

Stephen

Attachments:

catalog_function_acls_v3.patchtext/x-diff; charset=us-asciiDownload
From dd682d4d9dc7f25ae38bccccb5fc5ed5082c5071 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Thu, 19 Mar 2015 14:49:26 -0400
Subject: [PATCH] Use GRANT for access to privileged functions

Historically, we have hard-coded the permissions for privileged
functions in C code (eg: if (superuser()) then allow X, else don't).
Unfortunately, that's pretty limiting and means that functions which are
useful for monitoring require that the user calling them be a superuser.
That's a problem because, generally speaking, monitoring systems should
not have more access than they need and certainly should not have write
access to a system.

Thankfully, we have a very handy and complex privilege system for
managing who has access to what already built into PG.  This is the
GRANT system which has existed since very near the beginning of PG.
Therefore, provide a set of system functions which are not able to be
executed by all users by default and allow administrators to grant
access to those functions for the users (eg: monitoring or other roles)
where they feel it is appropriate.

We avoid breaking existing APIs for the system views by providing
backwards compatible functions which continue to filter in the C code
based on the user's credentials, where that is possible.

For a few functions (eg: pg_logical_slot_* and friends), it makes more
sense to break compatibility as they are relatively new and require
admins to GRANT access to those functions explicitly on upgrade (this
should be noted in the release notes).  Other functions, which allowed
access based on role attribute 'replication', may need to have GRANTs
applied to them, but note that replication connections do not go through
the normal GRANT system and therefore tools such as pg_basebackup will
not be impacted by this change.  In general, this change requires
administrators to be more explicit about which roles have access to
these functions.

Last, but certainly not least, this changes pg_dump to include ACLs for
the pg_catalog schema.  This is necessary as administrators are now
expected and encouraged to set privileges on functions and perhaps views
differently from their defaults and we need to preserve those settings.
---
 contrib/test_decoding/expected/permissions.out |   17 +-
 contrib/test_decoding/sql/permissions.sql      |    9 +
 src/backend/access/transam/xlogfuncs.c         |   30 -
 src/backend/catalog/system_views.sql           |   72 ++
 src/backend/replication/logical/logicalfuncs.c |   11 -
 src/backend/replication/slotfuncs.c            |   15 -
 src/backend/replication/walsender.c            |   86 +-
 src/backend/utils/adt/misc.c                   |   64 +-
 src/backend/utils/adt/pgstatfuncs.c            |  246 +++--
 src/bin/pg_dump/pg_dump.c                      | 1379 +++++++++++++-----------
 src/bin/pg_dump/pg_dump.h                      |   14 +-
 src/include/catalog/pg_proc.h                  |    7 +
 src/include/replication/walsender.h            |    1 +
 src/include/utils/builtins.h                   |    1 +
 src/test/regress/expected/rules.out            |   42 +
 15 files changed, 1170 insertions(+), 824 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..538ebdc 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -4,6 +4,13 @@ SET synchronous_commit = on;
 CREATE ROLE lr_normal;
 CREATE ROLE lr_superuser SUPERUSER;
 CREATE ROLE lr_replication REPLICATION;
+GRANT EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_drop_replication_slot(name) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_create_logical_replication_slot(name,name) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
 CREATE TABLE lr_test(data text);
 -- superuser can control replication
 SET ROLE lr_superuser;
@@ -54,13 +61,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_create_logical_replication_slot
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_logical_slot_get_changes
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_drop_replication_slot
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +97,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_drop_replication_slot
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
@@ -126,5 +133,7 @@ SELECT pg_drop_replication_slot('regression_slot');
 
 DROP ROLE lr_normal;
 DROP ROLE lr_superuser;
+SET client_min_messages TO 'warning';
+DROP OWNED BY lr_replication CASCADE;
 DROP ROLE lr_replication;
 DROP TABLE lr_test;
diff --git a/contrib/test_decoding/sql/permissions.sql b/contrib/test_decoding/sql/permissions.sql
index 8680c55..2c02d14 100644
--- a/contrib/test_decoding/sql/permissions.sql
+++ b/contrib/test_decoding/sql/permissions.sql
@@ -5,6 +5,13 @@ SET synchronous_commit = on;
 CREATE ROLE lr_normal;
 CREATE ROLE lr_superuser SUPERUSER;
 CREATE ROLE lr_replication REPLICATION;
+GRANT EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_drop_replication_slot(name) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_create_logical_replication_slot(name,name) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) TO lr_replication;
 CREATE TABLE lr_test(data text);
 
 -- superuser can control replication
@@ -65,5 +72,7 @@ SELECT pg_drop_replication_slot('regression_slot');
 
 DROP ROLE lr_normal;
 DROP ROLE lr_superuser;
+SET client_min_messages TO 'warning';
+DROP OWNED BY lr_replication CASCADE;
 DROP ROLE lr_replication;
 DROP TABLE lr_test;
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2179bf7..c9d9f3d 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -54,11 +54,6 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	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);
 
 	PG_RETURN_LSN(startpoint);
@@ -82,11 +77,6 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	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);
 
 	PG_RETURN_LSN(stoppoint);
@@ -100,11 +90,6 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
-
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -129,11 +114,6 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	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,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -338,11 +318,6 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
-
 	if (!RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -360,11 +335,6 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
-
 	if (!RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a4fd88f..947d077 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -623,6 +623,30 @@ CREATE VIEW pg_stat_activity AS
     WHERE S.datid = D.oid AND
             S.usesysid = U.oid;
 
+CREATE VIEW pg_stat_activity_all AS
+    SELECT
+            S.datid AS datid,
+            D.datname AS datname,
+            S.pid,
+            S.usesysid,
+            U.rolname AS usename,
+            S.application_name,
+            S.client_addr,
+            S.client_hostname,
+            S.client_port,
+            S.backend_start,
+            S.xact_start,
+            S.query_start,
+            S.state_change,
+            S.waiting,
+            S.state,
+            S.backend_xid,
+            s.backend_xmin,
+            S.query
+    FROM pg_database D, pg_stat_get_activity_all(NULL) AS S, pg_authid U
+    WHERE S.datid = D.oid AND
+            S.usesysid = U.oid;
+
 CREATE VIEW pg_stat_replication AS
     SELECT
             S.pid,
@@ -657,6 +681,29 @@ CREATE VIEW pg_stat_ssl AS
             S.sslclientdn AS clientdn
     FROM pg_stat_get_activity(NULL) AS S;
 
+CREATE VIEW pg_stat_replication_all AS
+    SELECT
+            S.pid,
+            S.usesysid,
+            U.rolname AS usename,
+            S.application_name,
+            S.client_addr,
+            S.client_hostname,
+            S.client_port,
+            S.backend_start,
+            S.backend_xmin,
+            W.state,
+            W.sent_location,
+            W.write_location,
+            W.flush_location,
+            W.replay_location,
+            W.sync_priority,
+            W.sync_state
+    FROM pg_stat_get_activity_all(NULL) AS S, pg_authid U,
+            pg_stat_get_wal_senders_all() AS W
+    WHERE S.usesysid = U.oid AND
+            S.pid = W.pid;
+
 CREATE VIEW pg_replication_slots AS
     SELECT
             L.slot_name,
@@ -908,3 +955,28 @@ RETURNS interval
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'make_interval';
+
+-- Revoke privileges for functions that should not be available to
+-- all users.  Administrators are allowed to change this later, if
+-- they wish.
+REVOKE EXECUTE ON FUNCTION pg_start_backup(text, boolean) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stop_backup() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_switch_xlog() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_create_restore_point(text) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_rotate_logfile() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_signal_backend(int, int) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stat_get_activity_all(integer) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stat_get_wal_senders_all() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_xlog_replay_pause() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_xlog_replay_resume() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_create_logical_replication_slot(name, name) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_drop_replication_slot(name) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_current_xlog_insert_location() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_last_xlog_receive_location() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_current_xlog_location() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_last_xlog_replay_location() FROM public;
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 3be5263..2995bfa 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -202,15 +202,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,8 +315,6 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
-
 	CheckLogicalDecodingRequirements();
 
 	arr = PG_GETARG_ARRAYTYPE_P(3);
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index f31925d..c879977 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -23,15 +23,6 @@
 #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.
@@ -51,8 +42,6 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
-
 	CheckSlotRequirements();
 
 	/* acquire replication slot, this will check for conflicting names */
@@ -94,8 +83,6 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
-
 	CheckLogicalDecodingRequirements();
 
 	/*
@@ -143,8 +130,6 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
-
 	CheckSlotRequirements();
 
 	ReplicationSlotDrop(NameStr(*name));
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4a20569..fb16eef 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -217,6 +217,7 @@ static XLogRecPtr WalSndWaitForWal(XLogRecPtr loc);
 
 static void XLogRead(char *buf, XLogRecPtr startptr, Size count);
 
+static void populate_pg_stat_get_wal_senders(TupleDesc tupdesc, Tuplestorestate *tupstore, bool filter);
 
 /* Initialize walsender process before entering the main command loop */
 void
@@ -2717,22 +2718,21 @@ WalSndGetStateString(WalSndState state)
 	return "UNKNOWN";
 }
 
+#define PG_STAT_GET_WAL_SENDERS_COLS	8
 
 /*
  * Returns activity of walsenders, including pids and xlog locations sent to
- * standby servers.
+ * standby servers.  Note that this version filters out the results unless the
+ * caller is a superuser.
  */
 Datum
 pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_WAL_SENDERS_COLS	8
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	TupleDesc	tupdesc;
 	Tuplestorestate *tupstore;
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
-	WalSnd	   *sync_standby;
-	int			i;
 
 	/* check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
@@ -2760,6 +2760,73 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 	MemoryContextSwitchTo(oldcontext);
 
 	/*
+	 * Populate the tuplestore.
+	 *
+	 * For non-superusers, we ask that the results be filtered.
+	 */
+	populate_pg_stat_get_wal_senders(tupdesc, tupstore, !superuser());
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
+ * Returns activity of walsenders, including pids and xlog locations sent to
+ * standby servers.  Note that this version does NOT filter out the results,
+ * therefore the permissions must be managed at the GRANT level.
+ */
+Datum
+pg_stat_get_wal_senders_all(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Populate the tuplestore */
+	populate_pg_stat_get_wal_senders(tupdesc, tupstore, false);
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+static void
+populate_pg_stat_get_wal_senders(TupleDesc tupdesc, Tuplestorestate *tupstore, bool filter)
+{
+	WalSnd	   *sync_standby;
+	int			i;
+
+	/*
 	 * Get the currently active synchronous standby.
 	 */
 	LWLockAcquire(SyncRepLock, LW_SHARED);
@@ -2794,11 +2861,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (filter)
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * When asked to filter record results, set all the rest of the
+			 * columns to NULL.
 			 */
 			MemSet(&nulls[1], true, PG_STAT_GET_WAL_SENDERS_COLS - 1);
 		}
@@ -2843,10 +2910,7 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
 
-	/* clean up and return the tuplestore */
-	tuplestore_donestoring(tupstore);
-
-	return (Datum) 0;
+	return;
 }
 
 /*
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 61d609f..0204da6 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -76,11 +76,19 @@ current_query(PG_FUNCTION_ARGS)
 }
 
 /*
- * Send a signal to another backend.
+ * Internal helper function for sending a signal to another backend.
  *
- * The signal is delivered if the user is either a superuser or the same
- * role as the backend being signaled. For "dangerous" signals, an explicit
- * check for superuser needs to be done prior to calling this function.
+ * The signal is delivered if the user is a superuser.  If the other backend
+ * is owned by a superuser role, then the calling user must be a superuser.
+ *
+ * When perm_check is passed in as true, then the user must be a member of
+ * the role which owns the backend being signaled.  For "dangerous" signals,
+ * an explicit check for superuser needs to be done prior to calling this
+ * function.
+ *
+ * When perm_check is passwd in as false, then no check of role membership is
+ * performed as the GRANT system is expected to have been used to manage access
+ * to calling the function which called us.
  *
  * Returns 0 on success, 1 on general failure, 2 on normal permission error
  * and 3 if the caller needs to be a superuser.
@@ -94,7 +102,7 @@ current_query(PG_FUNCTION_ARGS)
 #define SIGNAL_BACKEND_NOPERMISSION 2
 #define SIGNAL_BACKEND_NOSUPERUSER 3
 static int
-pg_signal_backend(int pid, int sig)
+pg_signal_backend_helper(int pid, int sig, bool perm_check)
 {
 	PGPROC	   *proc = BackendPidGetProc(pid);
 
@@ -122,7 +130,7 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (perm_check && !has_privs_of_role(GetUserId(), proc->roleId))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -150,6 +158,41 @@ pg_signal_backend(int pid, int sig)
 }
 
 /*
+ * Signal a backend process.  Permissions for this are managed by the GRANT
+ * system and therefore we do not do any extra permissions checks through
+ * this path.
+ *
+ * Note that only superusers can signal superuser-owned processes.
+ */
+Datum
+pg_signal_backend(PG_FUNCTION_ARGS)
+{
+	int			backend = PG_GETARG_INT32(0);
+	int			signal = PG_GETARG_INT32(1);
+	int			r;
+
+	/*
+	 * We only allow "safe" signals to be used through this, unless the user
+	 * is a superuser.
+	 */
+	if (!superuser() && signal != SIGINT && signal != SIGTERM)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be a superuser to send signals other than SIGINT and SIGTERM"))));
+
+	r = pg_signal_backend_helper(backend, signal, false);
+
+	if (r == SIGNAL_BACKEND_NOSUPERUSER)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be a superuser to cancel superuser query"))));
+
+	Assert (r != SIGNAL_BACKEND_NOPERMISSION);
+
+	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+}
+
+/*
  * Signal to cancel a backend process.  This is allowed if you are a member of
  * the role whose process is being canceled.
  *
@@ -158,7 +201,7 @@ pg_signal_backend(int pid, int sig)
 Datum
 pg_cancel_backend(PG_FUNCTION_ARGS)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
+	int			r = pg_signal_backend_helper(PG_GETARG_INT32(0), SIGINT, true);
 
 	if (r == SIGNAL_BACKEND_NOSUPERUSER)
 		ereport(ERROR,
@@ -182,7 +225,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 Datum
 pg_terminate_backend(PG_FUNCTION_ARGS)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
+	int			r = pg_signal_backend_helper(PG_GETARG_INT32(0), SIGTERM, true);
 
 	if (r == SIGNAL_BACKEND_NOSUPERUSER)
 		ereport(ERROR,
@@ -225,11 +268,6 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 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)
 	{
 		ereport(WARNING,
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index bbe94c3..ea9a9f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -53,6 +53,7 @@ extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
+extern Datum pg_stat_get_activity_all(PG_FUNCTION_ARGS);
 extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
@@ -126,6 +127,8 @@ extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS);
 /* Global bgwriter statistics, from bgwriter.c */
 extern PgStat_MsgBgWriter bgwriterStats;
 
+static void populate_pg_stat_get_activity(TupleDesc tupdesc, Tuplestorestate *tupstore, int pid, Oid calling_user);
+
 Datum
 pg_stat_get_numscans(PG_FUNCTION_ARGS)
 {
@@ -524,137 +527,146 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
 	}
 }
 
+/*
+ * Returns activity of other backends.  Note that this version filters out
+ * the results unless the caller is a member of the role of the backend being
+ * examined.
+ */
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc   tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
 
-	if (SRF_IS_FIRSTCALL())
-	{
-		MemoryContext oldcontext;
-		TupleDesc	tupdesc;
+	/*
+	 * Populate the tuplestore.
+	 *
+	 * For this path, we pass in the current GetUserId() result and have the
+	 * populate function filter the results based on that.
+	 */
+	populate_pg_stat_get_activity(tupdesc, tupstore, PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0), GetUserId());
 
-		funcctx = SRF_FIRSTCALL_INIT();
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
 
-		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-		tupdesc = CreateTemplateTupleDesc(22, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
-						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
-						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid",
-						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "state",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "query",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "waiting",
-						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "act_start",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "query_start",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "backend_start",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "state_change",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_addr",
-						   INETOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 13, "client_hostname",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
-						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 15, "backend_xid",
-						   XIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
-						   XIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 17, "ssl",
-						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 18, "sslversion",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 19, "sslcipher",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 20, "sslbits",
-						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 21, "sslcompression",
-						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 22, "sslclientdn",
-						   TEXTOID, -1, 0);
-
-		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
-
-		funcctx->user_fctx = palloc0(sizeof(int));
-		if (PG_ARGISNULL(0))
-		{
-			/* Get all backends */
-			funcctx->max_calls = pgstat_fetch_stat_numbackends();
-		}
-		else
-		{
-			/*
-			 * Get one backend - locate by pid.
-			 *
-			 * We lookup the backend early, so we can return zero rows if it
-			 * doesn't exist, instead of returning a single row full of NULLs.
-			 */
-			int			pid = PG_GETARG_INT32(0);
-			int			i;
-			int			n = pgstat_fetch_stat_numbackends();
+	return (Datum) 0;
+}
 
-			for (i = 1; i <= n; i++)
-			{
-				PgBackendStatus *be = pgstat_fetch_stat_beentry(i);
+/*
+ * Returns activity of other backends.  Note that this version does NOT
+ * filter the results and therefore the permissions at the SQL level must
+ * be REVOKE'd from public.
+ */
+Datum
+pg_stat_get_activity_all(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc   tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
 
-				if (be)
-				{
-					if (be->st_procpid == pid)
-					{
-						*(int *) (funcctx->user_fctx) = i;
-						break;
-					}
-				}
-			}
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
 
-			if (*(int *) (funcctx->user_fctx) == 0)
-				/* Pid not found, return zero rows */
-				funcctx->max_calls = 0;
-			else
-				funcctx->max_calls = 1;
-		}
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
 
-		MemoryContextSwitchTo(oldcontext);
-	}
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
 
-	/* stuff done on every call of the function */
-	funcctx = SRF_PERCALL_SETUP();
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Populate the tuplestore.
+	 *
+	 * For this path, we pass in the current GetUserId() result and have the
+	 * populate function filter the results based on that.
+	 */
+	populate_pg_stat_get_activity(tupdesc, tupstore, PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0), InvalidOid);
 
-	if (funcctx->call_cntr < funcctx->max_calls)
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+static void
+populate_pg_stat_get_activity(TupleDesc tupdesc, Tuplestorestate *tupstore, int pid, Oid calling_user)
+{
+	int num_backends = pgstat_fetch_stat_numbackends();
+	int curr_backend;
+
+	/* 1-based index */
+	for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
 	{
 		/* for each row */
 		Datum		values[22];
 		bool		nulls[22];
-		HeapTuple	tuple;
 		LocalPgBackendStatus *local_beentry;
 		PgBackendStatus *beentry;
 
 		MemSet(values, 0, sizeof(values));
 		MemSet(nulls, 0, sizeof(nulls));
 
-		if (*(int *) (funcctx->user_fctx) > 0)
+		if (pid != -1)
 		{
-			/* Get specific pid slot */
-			local_beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx));
-			beentry = &local_beentry->backendStatus;
-		}
-		else
-		{
-			/* Get the next one in the list */
-			local_beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1);	/* 1-based index */
-			beentry = &local_beentry->backendStatus;
+			/* Skip any which are not the one we're looking for. */
+			PgBackendStatus *be = pgstat_fetch_stat_beentry(curr_backend);
+
+			if (!be || be->st_procpid != pid)
+				continue;
+
 		}
+
+		/* Get the next one in the list */
+		local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+		if (!local_beentry)
+			continue;
+
+		beentry = &local_beentry->backendStatus;
 		if (!beentry)
 		{
 			int			i;
@@ -665,8 +677,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[5] = false;
 			values[5] = CStringGetTextDatum("<backend information not available>");
 
-			tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-			SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+			continue;
 		}
 
 		/* Values available to all callers */
@@ -704,7 +716,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (calling_user == InvalidOid ||
+			has_privs_of_role(calling_user, beentry->st_userid))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -839,15 +852,14 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[13] = true;
 		}
 
-		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
-	}
-	else
-	{
-		/* nothing left */
-		SRF_RETURN_DONE(funcctx);
+		/* If only a single backend was requested, and we found it, break. */
+		if (pid != -1)
+			break;
 	}
+
+	return;
 }
 
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7da5c41..5827911 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1235,16 +1235,20 @@ selectDumpableNamespace(NamespaceInfo *nsinfo)
 	 * namespaces. If specific namespaces are being dumped, dump just those
 	 * namespaces. Otherwise, dump all non-system namespaces.
 	 */
+
 	if (table_include_oids.head != NULL)
-		nsinfo->dobj.dump = false;
+		nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else if (schema_include_oids.head != NULL)
 		nsinfo->dobj.dump = simple_oid_list_member(&schema_include_oids,
-												   nsinfo->dobj.catId.oid);
+												   nsinfo->dobj.catId.oid) ?
+							DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
+	else if (strncmp(nsinfo->dobj.name, "pg_catalog", 10) == 0)
+		nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
 			 strcmp(nsinfo->dobj.name, "information_schema") == 0)
-		nsinfo->dobj.dump = false;
+		nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		nsinfo->dobj.dump = true;
+		nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
 
 	/*
 	 * In any case, a namespace can be excluded by an exclusion switch
@@ -1252,7 +1256,7 @@ selectDumpableNamespace(NamespaceInfo *nsinfo)
 	if (nsinfo->dobj.dump &&
 		simple_oid_list_member(&schema_exclude_oids,
 							   nsinfo->dobj.catId.oid))
-		nsinfo->dobj.dump = false;
+		nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1268,7 +1272,8 @@ selectDumpableTable(TableInfo *tbinfo)
 	 */
 	if (table_include_oids.head != NULL)
 		tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
-												   tbinfo->dobj.catId.oid);
+												   tbinfo->dobj.catId.oid) ?
+							DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 	else
 		tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump;
 
@@ -1278,7 +1283,7 @@ selectDumpableTable(TableInfo *tbinfo)
 	if (tbinfo->dobj.dump &&
 		simple_oid_list_member(&table_exclude_oids,
 							   tbinfo->dobj.catId.oid))
-		tbinfo->dobj.dump = false;
+		tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1307,7 +1312,7 @@ selectDumpableType(TypeInfo *tyinfo)
 		if (tytable != NULL)
 			tyinfo->dobj.dump = tytable->dobj.dump;
 		else
-			tyinfo->dobj.dump = false;
+			tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
 		return;
 	}
 
@@ -1324,16 +1329,11 @@ selectDumpableType(TypeInfo *tyinfo)
 		 */
 	}
 
-	/* dump only types in dumpable namespaces */
-	if (!tyinfo->dobj.namespace->dobj.dump)
-		tyinfo->dobj.dump = false;
-
 	/* skip undefined placeholder types */
-	else if (!tyinfo->isDefined)
-		tyinfo->dobj.dump = false;
-
+	if (!tyinfo->isDefined)
+		tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		tyinfo->dobj.dump = true;
+		tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump;
 }
 
 /*
@@ -1350,7 +1350,7 @@ selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
 	if (dinfo->dobj.namespace)
 		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
 	else
-		dinfo->dobj.dump = dopt->include_everything;
+		dinfo->dobj.dump = dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1366,9 +1366,9 @@ static void
 selectDumpableCast(DumpOptions *dopt, CastInfo *cast)
 {
 	if (cast->dobj.catId.oid < (Oid) FirstNormalObjectId)
-		cast->dobj.dump = false;
+		cast->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		cast->dobj.dump = dopt->include_everything;
+		cast->dobj.dump = dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1385,9 +1385,9 @@ static void
 selectDumpableExtension(DumpOptions *dopt, ExtensionInfo *extinfo)
 {
 	if (dopt->binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
-		extinfo->dobj.dump = false;
+		extinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		extinfo->dobj.dump = dopt->include_everything;
+		extinfo->dobj.dump = dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1809,13 +1809,14 @@ dumpTableData(Archive *fout, DumpOptions *dopt, TableDataInfo *tdinfo)
 	 * dependency on its table as "special" and pass it to ArchiveEntry now.
 	 * See comments for BuildArchiveDependencies.
 	 */
-	ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
-				 tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name,
-				 NULL, tbinfo->rolname,
-				 false, "TABLE DATA", SECTION_DATA,
-				 "", "", copyStmt,
-				 &(tbinfo->dobj.dumpId), 1,
-				 dumpFn, tdinfo);
+	if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
+		ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
+					 tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name,
+					 NULL, tbinfo->rolname,
+					 false, "TABLE DATA", SECTION_DATA,
+					 "", "", copyStmt,
+					 &(tbinfo->dobj.dumpId), 1,
+					 dumpFn, tdinfo);
 
 	destroyPQExpBuffer(copyBuf);
 	destroyPQExpBuffer(clistBuf);
@@ -1843,23 +1844,24 @@ refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
 	appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
 					  fmtId(tbinfo->dobj.name));
 
-	ArchiveEntry(fout,
-				 tdinfo->dobj.catId,	/* catalog ID */
-				 tdinfo->dobj.dumpId,	/* dump ID */
-				 tbinfo->dobj.name,		/* Name */
-				 tbinfo->dobj.namespace->dobj.name,		/* Namespace */
-				 NULL,			/* Tablespace */
-				 tbinfo->rolname,		/* Owner */
-				 false,			/* with oids */
-				 "MATERIALIZED VIEW DATA",		/* Desc */
-				 SECTION_POST_DATA,		/* Section */
-				 q->data,		/* Create */
-				 "",			/* Del */
-				 NULL,			/* Copy */
-				 tdinfo->dobj.dependencies,		/* Deps */
-				 tdinfo->dobj.nDeps,	/* # Deps */
-				 NULL,			/* Dumper */
-				 NULL);			/* Dumper Arg */
+	if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
+		ArchiveEntry(fout,
+					 tdinfo->dobj.catId,	/* catalog ID */
+					 tdinfo->dobj.dumpId,	/* dump ID */
+					 tbinfo->dobj.name,		/* Name */
+					 tbinfo->dobj.namespace->dobj.name,		/* Namespace */
+					 NULL,			/* Tablespace */
+					 tbinfo->rolname,		/* Owner */
+					 false,			/* with oids */
+					 "MATERIALIZED VIEW DATA",		/* Desc */
+					 SECTION_POST_DATA,		/* Section */
+					 q->data,		/* Create */
+					 "",			/* Del */
+					 NULL,			/* Copy */
+					 tdinfo->dobj.dependencies,		/* Deps */
+					 tdinfo->dobj.nDeps,	/* # Deps */
+					 NULL,			/* Dumper */
+					 NULL);			/* Dumper Arg */
 
 	destroyPQExpBuffer(q);
 }
@@ -1875,7 +1877,7 @@ getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, bool oids)
 
 	for (i = 0; i < numTables; i++)
 	{
-		if (tblinfo[i].dobj.dump)
+		if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA)
 			makeTableDataInfo(dopt, &(tblinfo[i]), oids);
 	}
 }
@@ -2685,31 +2687,38 @@ dumpBlob(Archive *fout, DumpOptions *dopt, BlobInfo *binfo)
 					  "SELECT pg_catalog.lo_unlink('%s');\n",
 					  binfo->dobj.name);
 
-	ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId,
-				 binfo->dobj.name,
-				 NULL, NULL,
-				 binfo->rolname, false,
-				 "BLOB", SECTION_PRE_DATA,
-				 cquery->data, dquery->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (binfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId,
+					 binfo->dobj.name,
+					 NULL, NULL,
+					 binfo->rolname, false,
+					 "BLOB", SECTION_PRE_DATA,
+					 cquery->data, dquery->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* set up tag for comment and/or ACL */
 	resetPQExpBuffer(cquery);
 	appendPQExpBuffer(cquery, "LARGE OBJECT %s", binfo->dobj.name);
 
 	/* Dump comment if any */
-	dumpComment(fout, dopt, cquery->data,
-				NULL, binfo->rolname,
-				binfo->dobj.catId, 0, binfo->dobj.dumpId);
+	if (binfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, cquery->data,
+					NULL, binfo->rolname,
+					binfo->dobj.catId, 0, binfo->dobj.dumpId);
 
 	/* Dump security label if any */
-	dumpSecLabel(fout, dopt, cquery->data,
-				 NULL, binfo->rolname,
-				 binfo->dobj.catId, 0, binfo->dobj.dumpId);
+	if (binfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, cquery->data,
+					 NULL, binfo->rolname,
+					 binfo->dobj.catId, 0, binfo->dobj.dumpId);
 
-	/* Dump ACL if any */
-	if (binfo->blobacl)
+	/*
+	 * Dump ACL if any.
+	 *
+	 * Note that we have to check if blobacl is NULL, dumpACL doesn't.
+	 */
+	if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL))
 		dumpACL(fout, dopt, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
 				binfo->dobj.name, NULL, cquery->data,
 				NULL, binfo->rolname, binfo->blobacl);
@@ -3660,7 +3669,7 @@ getTypes(Archive *fout, int *numTypes)
 			 * dump it if the I/O or canonicalize functions need to be dumped;
 			 * this is taken care of while sorting dependencies.
 			 */
-			stinfo->dobj.dump = false;
+			stinfo->dobj.dump = DUMP_COMPONENT_NONE;
 
 			/*
 			 * However, if dumping from pre-7.3, there will be no dependency
@@ -3687,7 +3696,7 @@ getTypes(Archive *fout, int *numTypes)
 					addObjectDependency(&funcInfo->dobj,
 										stinfo->dobj.dumpId);
 					/* mark shell type as to be dumped */
-					stinfo->dobj.dump = true;
+					stinfo->dobj.dump = DUMP_COMPONENT_ALL;
 				}
 
 				funcInfo = findFuncByOid(typoutput);
@@ -3700,7 +3709,7 @@ getTypes(Archive *fout, int *numTypes)
 					addObjectDependency(&funcInfo->dobj,
 										stinfo->dobj.dumpId);
 					/* mark shell type as to be dumped */
-					stinfo->dobj.dump = true;
+					stinfo->dobj.dump = DUMP_COMPONENT_ALL;
 				}
 			}
 		}
@@ -4364,11 +4373,11 @@ getFuncs(Archive *fout, DumpOptions *dopt, int *numFuncs)
 
 	/*
 	 * Find all user-defined functions.  Normally we can exclude functions in
-	 * pg_catalog, which is worth doing since there are several thousand of
-	 * 'em.  However, there are some extensions that create functions in
-	 * pg_catalog.  In normal dumps we can still ignore those --- but in
-	 * binary-upgrade mode, we must dump the member objects of the extension,
-	 * so be sure to fetch any such functions.
+	 * pg_catalog, provided their ACLs are still the default, which is worth
+	 * doing since there are several thousand of 'em.  However, there are
+	 * some extensions that create functions in pg_catalog.  In normal dumps we
+	 * can still ignore those --- but in binary-upgrade mode, we must dump the
+	 * member objects of the extension, so be sure to fetch any such functions.
 	 *
 	 * Also, in 9.2 and up, exclude functions that are internally dependent on
 	 * something else, since presumably those will be created as a result of
@@ -4387,9 +4396,13 @@ getFuncs(Archive *fout, DumpOptions *dopt, int *numFuncs)
 						  "(%s proowner) AS rolname "
 						  "FROM pg_proc p "
 						  "WHERE NOT proisagg AND ("
-						  "pronamespace != "
+						  "(pronamespace != "
 						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')",
+						  "WHERE nspname = 'pg_catalog') OR "
+						  "(pronamespace = "
+						  "(SELECT oid FROM pg_namespace "
+						  "WHERE nspname = 'pg_catalog') AND "
+						  "proacl IS NOT NULL))",
 						  username_subquery);
 		if (fout->remoteVersion >= 90200)
 			appendPQExpBufferStr(query,
@@ -5126,10 +5139,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 		 * Decide whether we want to dump this table.
 		 */
 		if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
-			tblinfo[i].dobj.dump = false;
+			tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
 		else
 			selectDumpableTable(&tblinfo[i]);
-		tblinfo[i].interesting = tblinfo[i].dobj.dump;
+		tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
 
 		tblinfo[i].postponed_def = false;		/* might get set during sort */
 
@@ -5202,7 +5215,7 @@ getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
 		if (owning_tab && owning_tab->dobj.dump)
 		{
 			seqinfo->interesting = true;
-			seqinfo->dobj.dump = true;
+			seqinfo->dobj.dump = DUMP_COMPONENT_ALL;
 		}
 	}
 }
@@ -8268,26 +8281,31 @@ dumpNamespace(Archive *fout, DumpOptions *dopt, NamespaceInfo *nspinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &nspinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
-				 nspinfo->dobj.name,
-				 NULL, NULL,
-				 nspinfo->rolname,
-				 false, "SCHEMA", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
+					 nspinfo->dobj.name,
+					 NULL, NULL,
+					 nspinfo->rolname,
+					 false, "SCHEMA", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Schema Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, nspinfo->rolname,
-				nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 NULL, nspinfo->rolname,
-				 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, nspinfo->rolname,
+					nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
-			qnspname, NULL, nspinfo->dobj.name, NULL,
-			nspinfo->rolname, nspinfo->nspacl);
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 NULL, nspinfo->rolname,
+					 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
+				qnspname, NULL, nspinfo->dobj.name, NULL,
+				nspinfo->rolname, nspinfo->nspacl);
 
 	free(qnspname);
 
@@ -8396,22 +8414,26 @@ dumpExtension(Archive *fout, DumpOptions *dopt, ExtensionInfo *extinfo)
 
 	appendPQExpBuffer(labelq, "EXTENSION %s", qextname);
 
-	ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
-				 extinfo->dobj.name,
-				 NULL, NULL,
-				 "",
-				 false, "EXTENSION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
+					 extinfo->dobj.name,
+					 NULL, NULL,
+					 "",
+					 false, "EXTENSION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Extension Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 NULL, "",
-				 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+	if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+
+	if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 NULL, "",
+					 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
 
 	free(qextname);
 
@@ -8442,6 +8464,8 @@ dumpType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 		dumpEnumType(fout, dopt, tyinfo);
 	else if (tyinfo->typtype == TYPTYPE_RANGE)
 		dumpRangeType(fout, dopt, tyinfo);
+	else if (tyinfo->typtype == TYPTYPE_PSEUDO) /* can't */
+		return;
 	else
 		write_msg(NULL, "WARNING: typtype of data type \"%s\" appears to be invalid\n",
 				  tyinfo->dobj.name);
@@ -8546,28 +8570,33 @@ dumpEnumType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -8678,28 +8707,33 @@ dumpRangeType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -9068,28 +9102,33 @@ dumpBaseType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -9230,28 +9269,33 @@ dumpDomain(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "DOMAIN", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "DOMAIN", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Domain Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	/* Dump any per-constraint comments */
 	for (i = 0; i < tyinfo->nDomChecks; i++)
@@ -9263,10 +9307,13 @@ dumpDomain(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 						  fmtId(domcheck->dobj.name));
 		appendPQExpBuffer(labelq, "ON DOMAIN %s",
 						  fmtId(qtypname));
-		dumpComment(fout, dopt, labelq->data,
-					tyinfo->dobj.namespace->dobj.name,
-					tyinfo->rolname,
-					domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+		if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+			dumpComment(fout, dopt, labelq->data,
+						tyinfo->dobj.namespace->dobj.name,
+						tyinfo->rolname,
+						domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+
 		destroyPQExpBuffer(labelq);
 	}
 
@@ -9455,29 +9502,34 @@ dumpCompositeType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -9487,7 +9539,8 @@ dumpCompositeType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	destroyPQExpBuffer(query);
 
 	/* Dump any per-column comments */
-	dumpCompositeTypeColComments(fout, tyinfo);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpCompositeTypeColComments(fout, tyinfo);
 }
 
 /*
@@ -9631,15 +9684,16 @@ dumpShellType(Archive *fout, DumpOptions *dopt, ShellTypeInfo *stinfo)
 	appendPQExpBuffer(q, "CREATE TYPE %s;\n",
 					  fmtId(stinfo->dobj.name));
 
-	ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
-				 stinfo->dobj.name,
-				 stinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 stinfo->baseType->rolname, false,
-				 "SHELL TYPE", SECTION_PRE_DATA,
-				 q->data, "", NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
+					 stinfo->dobj.name,
+					 stinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 stinfo->baseType->rolname, false,
+					 "SHELL TYPE", SECTION_PRE_DATA,
+					 q->data, "", NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	destroyPQExpBuffer(q);
 }
@@ -9805,23 +9859,27 @@ dumpProcLang(Archive *fout, DumpOptions *dopt, ProcLangInfo *plang)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(defqry, &plang->dobj, labelq->data);
 
-	ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
-				 plang->dobj.name,
-				 lanschema, NULL, plang->lanowner,
-				 false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
-				 defqry->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
+					 plang->dobj.name,
+					 lanschema, NULL, plang->lanowner,
+					 false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
+					 defqry->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Proc Lang Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				plang->dobj.catId, 0, plang->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 NULL, "",
-				 plang->dobj.catId, 0, plang->dobj.dumpId);
-
-	if (plang->lanpltrusted)
+	if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					plang->dobj.catId, 0, plang->dobj.dumpId);
+
+	if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 NULL, "",
+					 plang->dobj.catId, 0, plang->dobj.dumpId);
+
+	if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
 		dumpACL(fout, dopt, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
 				qlanname, NULL, plang->dobj.name,
 				lanschema,
@@ -10393,28 +10451,33 @@ dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &finfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
-				 funcsig_tag,
-				 finfo->dobj.namespace->dobj.name,
-				 NULL,
-				 finfo->rolname, false,
-				 "FUNCTION", SECTION_PRE_DATA,
-				 q->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
+					 funcsig_tag,
+					 finfo->dobj.namespace->dobj.name,
+					 NULL,
+					 finfo->rolname, false,
+					 "FUNCTION", SECTION_PRE_DATA,
+					 q->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
-	/* Dump Function Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				finfo->dobj.namespace->dobj.name, finfo->rolname,
-				finfo->dobj.catId, 0, finfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 finfo->dobj.namespace->dobj.name, finfo->rolname,
-				 finfo->dobj.catId, 0, finfo->dobj.dumpId);
+		/* Dump Function Comments and Security Labels */
+	if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					finfo->dobj.namespace->dobj.name, finfo->rolname,
+					finfo->dobj.catId, 0, finfo->dobj.dumpId);
+
+	if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 finfo->dobj.namespace->dobj.name, finfo->rolname,
+					 finfo->dobj.catId, 0, finfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
-			funcsig, NULL, funcsig_tag,
-			finfo->dobj.namespace->dobj.name,
-			finfo->rolname, finfo->proacl);
+	if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
+				funcsig, NULL, funcsig_tag,
+				finfo->dobj.namespace->dobj.name,
+				finfo->rolname, finfo->proacl);
 
 	PQclear(res);
 
@@ -10521,18 +10584,20 @@ dumpCast(Archive *fout, DumpOptions *dopt, CastInfo *cast)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(defqry, &cast->dobj, labelq->data);
 
-	ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
-				 labelq->data,
-				 "pg_catalog", NULL, "",
-				 false, "CAST", SECTION_PRE_DATA,
-				 defqry->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
+					 labelq->data,
+					 "pg_catalog", NULL, "",
+					 false, "CAST", SECTION_PRE_DATA,
+					 defqry->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Cast Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				cast->dobj.catId, 0, cast->dobj.dumpId);
+	if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					cast->dobj.catId, 0, cast->dobj.dumpId);
 
 	destroyPQExpBuffer(defqry);
 	destroyPQExpBuffer(delqry);
@@ -10771,20 +10836,22 @@ dumpOpr(Archive *fout, DumpOptions *dopt, OprInfo *oprinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &oprinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
-				 oprinfo->dobj.name,
-				 oprinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 oprinfo->rolname,
-				 false, "OPERATOR", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
+					 oprinfo->dobj.name,
+					 oprinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 oprinfo->rolname,
+					 false, "OPERATOR", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Operator Comments */
-	dumpComment(fout, dopt, labelq->data,
-				oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
-				oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
+	if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
+					oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -11279,20 +11346,22 @@ dumpOpclass(Archive *fout, DumpOptions *dopt, OpclassInfo *opcinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &opcinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
-				 opcinfo->dobj.name,
-				 opcinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 opcinfo->rolname,
-				 false, "OPERATOR CLASS", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
+					 opcinfo->dobj.name,
+					 opcinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 opcinfo->rolname,
+					 false, "OPERATOR CLASS", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Operator Class Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, opcinfo->rolname,
-				opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
+	if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, opcinfo->rolname,
+					opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
 
 	free(amname);
 	destroyPQExpBuffer(query);
@@ -11592,20 +11661,22 @@ dumpOpfamily(Archive *fout, DumpOptions *dopt, OpfamilyInfo *opfinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &opfinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
-				 opfinfo->dobj.name,
-				 opfinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 opfinfo->rolname,
-				 false, "OPERATOR FAMILY", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
+					 opfinfo->dobj.name,
+					 opfinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 opfinfo->rolname,
+					 false, "OPERATOR FAMILY", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Operator Family Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, opfinfo->rolname,
-				opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
+	if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, opfinfo->rolname,
+					opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
 
 	free(amname);
 	PQclear(res_ops);
@@ -11681,20 +11752,22 @@ dumpCollation(Archive *fout, DumpOptions *dopt, CollInfo *collinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &collinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
-				 collinfo->dobj.name,
-				 collinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 collinfo->rolname,
-				 false, "COLLATION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
+					 collinfo->dobj.name,
+					 collinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 collinfo->rolname,
+					 false, "COLLATION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Collation Comments */
-	dumpComment(fout, dopt, labelq->data,
-				collinfo->dobj.namespace->dobj.name, collinfo->rolname,
-				collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
+	if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					collinfo->dobj.namespace->dobj.name, collinfo->rolname,
+					collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -11780,20 +11853,22 @@ dumpConversion(Archive *fout, DumpOptions *dopt, ConvInfo *convinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &convinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
-				 convinfo->dobj.name,
-				 convinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 convinfo->rolname,
-				 false, "CONVERSION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
+					 convinfo->dobj.name,
+					 convinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 convinfo->rolname,
+					 false, "CONVERSION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Conversion Comments */
-	dumpComment(fout, dopt, labelq->data,
-				convinfo->dobj.namespace->dobj.name, convinfo->rolname,
-				convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
+	if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					convinfo->dobj.namespace->dobj.name, convinfo->rolname,
+					convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -12176,23 +12251,27 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &agginfo->aggfn.dobj, labelq->data);
 
-	ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
-				 aggsig_tag,
-				 agginfo->aggfn.dobj.namespace->dobj.name,
-				 NULL,
-				 agginfo->aggfn.rolname,
-				 false, "AGGREGATE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
+					 aggsig_tag,
+					 agginfo->aggfn.dobj.namespace->dobj.name,
+					 NULL,
+					 agginfo->aggfn.rolname,
+					 false, "AGGREGATE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Aggregate Comments */
-	dumpComment(fout, dopt, labelq->data,
-			agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
-				agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-			agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
-				 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+				agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+					agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+				agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+					 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
 
 	/*
 	 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
@@ -12205,11 +12284,12 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo)
 	aggsig = format_function_signature(fout, &agginfo->aggfn, true);
 	aggsig_tag = format_function_signature(fout, &agginfo->aggfn, false);
 
-	dumpACL(fout, dopt, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
-			"FUNCTION",
-			aggsig, NULL, aggsig_tag,
-			agginfo->aggfn.dobj.namespace->dobj.name,
-			agginfo->aggfn.rolname, agginfo->aggfn.proacl);
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
+				"FUNCTION",
+				aggsig, NULL, aggsig_tag,
+				agginfo->aggfn.dobj.namespace->dobj.name,
+				agginfo->aggfn.rolname, agginfo->aggfn.proacl);
 
 	free(aggsig);
 	if (aggfullsig)
@@ -12276,20 +12356,22 @@ dumpTSParser(Archive *fout, DumpOptions *dopt, TSParserInfo *prsinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &prsinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
-				 prsinfo->dobj.name,
-				 prsinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 "",
-				 false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
+					 prsinfo->dobj.name,
+					 prsinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 "",
+					 false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Parser Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
+	if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12363,20 +12445,22 @@ dumpTSDictionary(Archive *fout, DumpOptions *dopt, TSDictInfo *dictinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &dictinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
-				 dictinfo->dobj.name,
-				 dictinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 dictinfo->rolname,
-				 false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
+					 dictinfo->dobj.name,
+					 dictinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 dictinfo->rolname,
+					 false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Dictionary Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, dictinfo->rolname,
-				dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
+	if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, dictinfo->rolname,
+					dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12429,20 +12513,22 @@ dumpTSTemplate(Archive *fout, DumpOptions *dopt, TSTemplateInfo *tmplinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tmplinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
-				 tmplinfo->dobj.name,
-				 tmplinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 "",
-				 false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
+					 tmplinfo->dobj.name,
+					 tmplinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 "",
+					 false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Template Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
+	if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12557,20 +12643,22 @@ dumpTSConfig(Archive *fout, DumpOptions *dopt, TSConfigInfo *cfginfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &cfginfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
-				 cfginfo->dobj.name,
-				 cfginfo->dobj.namespace->dobj.name,
-				 NULL,
-				 cfginfo->rolname,
-				 false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
+					 cfginfo->dobj.name,
+					 cfginfo->dobj.namespace->dobj.name,
+					 NULL,
+					 cfginfo->rolname,
+					 false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Configuration Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, cfginfo->rolname,
-				cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
+	if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, cfginfo->rolname,
+					cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12631,27 +12719,30 @@ dumpForeignDataWrapper(Archive *fout, DumpOptions *dopt, FdwInfo *fdwinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &fdwinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
-				 fdwinfo->dobj.name,
-				 NULL,
-				 NULL,
-				 fdwinfo->rolname,
-				 false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
+					 fdwinfo->dobj.name,
+					 NULL,
+					 NULL,
+					 fdwinfo->rolname,
+					 false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Handle the ACL */
-	dumpACL(fout, dopt, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
-			"FOREIGN DATA WRAPPER",
-			qfdwname, NULL, fdwinfo->dobj.name,
-			NULL, fdwinfo->rolname,
-			fdwinfo->fdwacl);
+	if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
+				"FOREIGN DATA WRAPPER",
+				qfdwname, NULL, fdwinfo->dobj.name,
+				NULL, fdwinfo->rolname,
+				fdwinfo->fdwacl);
 
 	/* Dump Foreign Data Wrapper Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, fdwinfo->rolname,
-				fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
+	if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, fdwinfo->rolname,
+					fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
 
 	free(qfdwname);
 
@@ -12723,33 +12814,37 @@ dumpForeignServer(Archive *fout, DumpOptions *dopt, ForeignServerInfo *srvinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &srvinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
-				 srvinfo->dobj.name,
-				 NULL,
-				 NULL,
-				 srvinfo->rolname,
-				 false, "SERVER", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
+					 srvinfo->dobj.name,
+					 NULL,
+					 NULL,
+					 srvinfo->rolname,
+					 false, "SERVER", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Handle the ACL */
-	dumpACL(fout, dopt, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
-			"FOREIGN SERVER",
-			qsrvname, NULL, srvinfo->dobj.name,
-			NULL, srvinfo->rolname,
-			srvinfo->srvacl);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
+				"FOREIGN SERVER",
+				qsrvname, NULL, srvinfo->dobj.name,
+				NULL, srvinfo->rolname,
+				srvinfo->srvacl);
 
 	/* Dump user mappings */
-	dumpUserMappings(fout,
-					 srvinfo->dobj.name, NULL,
-					 srvinfo->rolname,
-					 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
+		dumpUserMappings(fout,
+						 srvinfo->dobj.name, NULL,
+						 srvinfo->rolname,
+						 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
 
 	/* Dump Foreign Server Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, srvinfo->rolname,
-				srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, srvinfo->rolname,
+					srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
 
 	free(qsrvname);
 
@@ -12911,15 +13006,16 @@ dumpDefaultACL(Archive *fout, DumpOptions *dopt, DefaultACLInfo *daclinfo)
 		exit_horribly(NULL, "could not parse default ACL list (%s)\n",
 					  daclinfo->defaclacl);
 
-	ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
-				 tag->data,
+	if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
+					 tag->data,
 	   daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL,
-				 NULL,
-				 daclinfo->defaclrole,
-				 false, "DEFAULT ACL", SECTION_POST_DATA,
-				 q->data, "", NULL,
-				 NULL, 0,
-				 NULL, NULL);
+					 NULL,
+					 daclinfo->defaclrole,
+					 false, "DEFAULT ACL", SECTION_POST_DATA,
+					 q->data, "", NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	destroyPQExpBuffer(tag);
 	destroyPQExpBuffer(q);
@@ -13285,17 +13381,20 @@ collectSecLabels(Archive *fout, SecLabelItem **items)
 static void
 dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 {
-	if (tbinfo->dobj.dump && !dopt->dataOnly)
-	{
-		char	   *namecopy;
+	char	   *namecopy;
 
-		if (tbinfo->relkind == RELKIND_SEQUENCE)
-			dumpSequence(fout, dopt, tbinfo);
-		else
-			dumpTableSchema(fout, dopt, tbinfo);
+	if (!(tbinfo->dobj.dump & (DUMP_COMPONENT_DEFINITION|DUMP_COMPONENT_ACL)))
+		return;
 
-		/* Handle the ACL here */
-		namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
+	if (tbinfo->relkind == RELKIND_SEQUENCE)
+		dumpSequence(fout, dopt, tbinfo);
+	else
+		dumpTableSchema(fout, dopt, tbinfo);
+
+	namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
+
+	/* Handle the ACL here, if requested */
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
 		dumpACL(fout, dopt, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
 				(tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" :
 				"TABLE",
@@ -13303,47 +13402,51 @@ dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				tbinfo->relacl);
 
-		/*
-		 * Handle column ACLs, if any.  Note: we pull these with a separate
-		 * query rather than trying to fetch them during getTableAttrs, so
-		 * that we won't miss ACLs on system columns.
-		 */
-		if (fout->remoteVersion >= 80400)
-		{
-			PQExpBuffer query = createPQExpBuffer();
-			PGresult   *res;
-			int			i;
+	/*
+	 * Handle column ACLs, if any.  Note: we pull these with a separate
+	 * query rather than trying to fetch them during getTableAttrs, so
+	 * that we won't miss ACLs on system columns.
+	 */
+	if (fout->remoteVersion >= 80400)
+	{
+		PQExpBuffer query = createPQExpBuffer();
+		PGresult   *res;
+		int			i;
 
-			appendPQExpBuffer(query,
-					   "SELECT attname, attacl FROM pg_catalog.pg_attribute "
-							  "WHERE attrelid = '%u' AND NOT attisdropped AND attacl IS NOT NULL "
-							  "ORDER BY attnum",
-							  tbinfo->dobj.catId.oid);
-			res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+		appendPQExpBuffer(query,
+				   "SELECT attname, attacl FROM pg_catalog.pg_attribute "
+						  "WHERE attrelid = '%u' AND NOT attisdropped AND attacl IS NOT NULL "
+						  "ORDER BY attnum",
+						  tbinfo->dobj.catId.oid);
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-			for (i = 0; i < PQntuples(res); i++)
-			{
-				char	   *attname = PQgetvalue(res, i, 0);
-				char	   *attacl = PQgetvalue(res, i, 1);
-				char	   *attnamecopy;
-				char	   *acltag;
+		for (i = 0; i < PQntuples(res); i++)
+		{
+			char	   *attname = PQgetvalue(res, i, 0);
+			char	   *attacl = PQgetvalue(res, i, 1);
+			char	   *attnamecopy;
+			char	   *acltag;
+
+			attnamecopy = pg_strdup(fmtId(attname));
+			acltag = psprintf("%s.%s", tbinfo->dobj.name, attname);
 
-				attnamecopy = pg_strdup(fmtId(attname));
-				acltag = psprintf("%s.%s", tbinfo->dobj.name, attname);
+			if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
 				/* Column's GRANT type is always TABLE */
 				dumpACL(fout, dopt, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
 						namecopy, attnamecopy, acltag,
 						tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 						attacl);
-				free(attnamecopy);
-				free(acltag);
-			}
-			PQclear(res);
-			destroyPQExpBuffer(query);
-		}
 
-		free(namecopy);
+			free(attnamecopy);
+			free(acltag);
+		}
+		PQclear(res);
+		destroyPQExpBuffer(query);
 	}
+
+	free(namecopy);
+
+	return;
 }
 
 /*
@@ -14011,24 +14114,27 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
-				 tbinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+					 tbinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
 			(tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
-				 tbinfo->rolname,
+					 tbinfo->rolname,
 			   (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
-				 reltypename,
-				 tbinfo->postponed_def ? SECTION_POST_DATA : SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+					 reltypename,
+					 tbinfo->postponed_def ? SECTION_POST_DATA : SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 
 	/* Dump Table Comments */
-	dumpTableComment(fout, dopt, tbinfo, reltypename);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpTableComment(fout, dopt, tbinfo, reltypename);
 
 	/* Dump Table Security Labels */
-	dumpTableSecLabel(fout, dopt, tbinfo, reltypename);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpTableSecLabel(fout, dopt, tbinfo, reltypename);
 
 	/* Dump comments on inlined table constraints */
 	for (j = 0; j < tbinfo->ncheck; j++)
@@ -14038,7 +14144,8 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 		if (constr->separate || !constr->conislocal)
 			continue;
 
-		dumpTableConstraintComment(fout, dopt, constr);
+		if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
+			dumpTableConstraintComment(fout, dopt, constr);
 	}
 
 	destroyPQExpBuffer(q);
@@ -14084,15 +14191,16 @@ dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo)
 	appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n",
 					  fmtId(tbinfo->attnames[adnum - 1]));
 
-	ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
-				 tbinfo->attnames[adnum - 1],
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname,
-				 false, "DEFAULT", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
+					 tbinfo->attnames[adnum - 1],
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname,
+					 false, "DEFAULT", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -14148,6 +14256,9 @@ dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo)
 	if (dopt->dataOnly)
 		return;
 
+	if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+		return;
+
 	q = createPQExpBuffer();
 	delq = createPQExpBuffer();
 	labelq = createPQExpBuffer();
@@ -14198,24 +14309,26 @@ dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo)
 		appendPQExpBuffer(delq, "%s;\n",
 						  fmtId(indxinfo->dobj.name));
 
-		ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
-					 indxinfo->dobj.name,
-					 tbinfo->dobj.namespace->dobj.name,
-					 indxinfo->tablespace,
-					 tbinfo->rolname, false,
-					 "INDEX", SECTION_POST_DATA,
-					 q->data, delq->data, NULL,
-					 NULL, 0,
-					 NULL, NULL);
+		if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+			ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
+						 indxinfo->dobj.name,
+						 tbinfo->dobj.namespace->dobj.name,
+						 indxinfo->tablespace,
+						 tbinfo->rolname, false,
+						 "INDEX", SECTION_POST_DATA,
+						 q->data, delq->data, NULL,
+						 NULL, 0,
+						 NULL, NULL);
 	}
 
 	/* Dump Index Comments */
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name,
-				tbinfo->rolname,
-				indxinfo->dobj.catId, 0,
-				is_constraint ? indxinfo->indexconstraint :
-				indxinfo->dobj.dumpId);
+	if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name,
+					tbinfo->rolname,
+					indxinfo->dobj.catId, 0,
+					is_constraint ? indxinfo->indexconstraint :
+					indxinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -14321,15 +14434,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 						  fmtId(coninfo->dobj.name));
 
-		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-					 coninfo->dobj.name,
-					 tbinfo->dobj.namespace->dobj.name,
-					 indxinfo->tablespace,
-					 tbinfo->rolname, false,
-					 "CONSTRAINT", SECTION_POST_DATA,
-					 q->data, delq->data, NULL,
-					 NULL, 0,
-					 NULL, NULL);
+		if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+						 coninfo->dobj.name,
+						 tbinfo->dobj.namespace->dobj.name,
+						 indxinfo->tablespace,
+						 tbinfo->rolname, false,
+						 "CONSTRAINT", SECTION_POST_DATA,
+						 q->data, delq->data, NULL,
+						 NULL, 0,
+						 NULL, NULL);
 	}
 	else if (coninfo->contype == 'f')
 	{
@@ -14354,15 +14468,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 						  fmtId(coninfo->dobj.name));
 
-		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-					 coninfo->dobj.name,
-					 tbinfo->dobj.namespace->dobj.name,
-					 NULL,
-					 tbinfo->rolname, false,
-					 "FK CONSTRAINT", SECTION_POST_DATA,
-					 q->data, delq->data, NULL,
-					 NULL, 0,
-					 NULL, NULL);
+		if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+						 coninfo->dobj.name,
+						 tbinfo->dobj.namespace->dobj.name,
+						 NULL,
+						 tbinfo->rolname, false,
+						 "FK CONSTRAINT", SECTION_POST_DATA,
+						 q->data, delq->data, NULL,
+						 NULL, 0,
+						 NULL, NULL);
 	}
 	else if (coninfo->contype == 'c' && tbinfo)
 	{
@@ -14389,15 +14504,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 							  fmtId(coninfo->dobj.name));
 
-			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-						 coninfo->dobj.name,
-						 tbinfo->dobj.namespace->dobj.name,
-						 NULL,
-						 tbinfo->rolname, false,
-						 "CHECK CONSTRAINT", SECTION_POST_DATA,
-						 q->data, delq->data, NULL,
-						 NULL, 0,
-						 NULL, NULL);
+			if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+				ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+							 coninfo->dobj.name,
+							 tbinfo->dobj.namespace->dobj.name,
+							 NULL,
+							 tbinfo->rolname, false,
+							 "CHECK CONSTRAINT", SECTION_POST_DATA,
+							 q->data, delq->data, NULL,
+							 NULL, 0,
+							 NULL, NULL);
 		}
 	}
 	else if (coninfo->contype == 'c' && tbinfo == NULL)
@@ -14425,15 +14541,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 							  fmtId(coninfo->dobj.name));
 
-			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-						 coninfo->dobj.name,
-						 tyinfo->dobj.namespace->dobj.name,
-						 NULL,
-						 tyinfo->rolname, false,
-						 "CHECK CONSTRAINT", SECTION_POST_DATA,
-						 q->data, delq->data, NULL,
-						 NULL, 0,
-						 NULL, NULL);
+			if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+				ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+							 coninfo->dobj.name,
+							 tyinfo->dobj.namespace->dobj.name,
+							 NULL,
+							 tyinfo->rolname, false,
+							 "CHECK CONSTRAINT", SECTION_POST_DATA,
+							 q->data, delq->data, NULL,
+							 NULL, 0,
+							 NULL, NULL);
 		}
 	}
 	else
@@ -14443,7 +14560,7 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 	}
 
 	/* Dump Constraint Comments --- only works for table constraints */
-	if (tbinfo && coninfo->separate)
+	if (tbinfo && coninfo->separate && coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
 		dumpTableConstraintComment(fout, dopt, coninfo);
 
 	destroyPQExpBuffer(q);
@@ -14467,11 +14584,17 @@ dumpTableConstraintComment(Archive *fout, DumpOptions *dopt, ConstraintInfo *con
 					  fmtId(coninfo->dobj.name));
 	appendPQExpBuffer(labelq, "ON %s",
 					  fmtId(tbinfo->dobj.name));
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name,
-				tbinfo->rolname,
-				coninfo->dobj.catId, 0,
-			 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
+
+	/*
+	 * The callers check this before calling us, so this shouldn't be necessary,
+	 * but it doesn't hurt to double-check here.
+	 */
+	if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name,
+					tbinfo->rolname,
+					coninfo->dobj.catId, 0,
+				 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(labelq);
 }
@@ -14664,15 +14787,16 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 		binary_upgrade_extension_member(query, &tbinfo->dobj,
 										labelq->data);
 
-	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
-				 tbinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname,
-				 false, "SEQUENCE", SECTION_PRE_DATA,
-				 query->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+					 tbinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname,
+					 false, "SEQUENCE", SECTION_PRE_DATA,
+					 query->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/*
 	 * If the sequence is owned by a table column, emit the ALTER for it as a
@@ -14700,25 +14824,29 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			appendPQExpBuffer(query, ".%s;\n",
 						fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
 
-			ArchiveEntry(fout, nilCatalogId, createDumpId(),
-						 tbinfo->dobj.name,
-						 tbinfo->dobj.namespace->dobj.name,
-						 NULL,
-						 tbinfo->rolname,
-						 false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
-						 query->data, "", NULL,
-						 &(tbinfo->dobj.dumpId), 1,
-						 NULL, NULL);
+			if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+				ArchiveEntry(fout, nilCatalogId, createDumpId(),
+							 tbinfo->dobj.name,
+							 tbinfo->dobj.namespace->dobj.name,
+							 NULL,
+							 tbinfo->rolname,
+							 false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
+							 query->data, "", NULL,
+							 &(tbinfo->dobj.dumpId), 1,
+							 NULL, NULL);
 		}
 	}
 
 	/* Dump Sequence Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-				tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+					tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+					 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -14767,15 +14895,16 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 	appendPQExpBuffer(query, ", %s, %s);\n",
 					  last, (called ? "true" : "false"));
 
-	ArchiveEntry(fout, nilCatalogId, createDumpId(),
-				 tbinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname,
-				 false, "SEQUENCE SET", SECTION_DATA,
-				 query->data, "", NULL,
-				 &(tbinfo->dobj.dumpId), 1,
-				 NULL, NULL);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_DATA)
+		ArchiveEntry(fout, nilCatalogId, createDumpId(),
+					 tbinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname,
+					 false, "SEQUENCE SET", SECTION_DATA,
+					 query->data, "", NULL,
+					 &(tbinfo->dobj.dumpId), 1,
+					 NULL, NULL);
 
 	PQclear(res);
 
@@ -14973,19 +15102,21 @@ dumpTrigger(Archive *fout, DumpOptions *dopt, TriggerInfo *tginfo)
 	appendPQExpBuffer(labelq, "ON %s",
 					  fmtId(tbinfo->dobj.name));
 
-	ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
-				 tginfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname, false,
-				 "TRIGGER", SECTION_POST_DATA,
-				 query->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
+					 tginfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname, false,
+					 "TRIGGER", SECTION_POST_DATA,
+					 query->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-				tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
+	if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+					tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
 
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
@@ -15050,14 +15181,16 @@ dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo *evtinfo)
 	appendPQExpBuffer(labelq, "EVENT TRIGGER %s",
 					  fmtId(evtinfo->dobj.name));
 
-	ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
-				 evtinfo->dobj.name, NULL, NULL, evtinfo->evtowner, false,
-				 "EVENT TRIGGER", SECTION_POST_DATA,
-				 query->data, "", NULL, NULL, 0, NULL, NULL);
+	if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
+					 evtinfo->dobj.name, NULL, NULL, evtinfo->evtowner, false,
+					 "EVENT TRIGGER", SECTION_POST_DATA,
+					 query->data, "", NULL, NULL, 0, NULL, NULL);
 
-	dumpComment(fout, dopt, labelq->data,
-				NULL, evtinfo->evtowner,
-				evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
+	if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, evtinfo->evtowner,
+					evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(labelq);
@@ -15172,21 +15305,23 @@ dumpRule(Archive *fout, DumpOptions *dopt, RuleInfo *rinfo)
 	appendPQExpBuffer(labelq, " ON %s",
 					  fmtId(tbinfo->dobj.name));
 
-	ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
-				 rinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname, false,
-				 "RULE", SECTION_POST_DATA,
-				 cmd->data, delcmd->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
+					 rinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname, false,
+					 "RULE", SECTION_POST_DATA,
+					 cmd->data, delcmd->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump rule comments */
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name,
-				tbinfo->rolname,
-				rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
+	if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name,
+					tbinfo->rolname,
+					rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
 
 	PQclear(res);
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index a9d3c10..37d33d0 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -79,6 +79,18 @@ typedef enum
 	DO_POLICY
 } DumpableObjectType;
 
+/* component types of an object which can be selected for dumping */
+typedef uint32 DumpComponents;	/* a bitmask of dump object components */
+#define DUMP_COMPONENT_NONE			(0)
+#define DUMP_COMPONENT_DEFINITION	(1 << 0)
+#define DUMP_COMPONENT_DATA			(1 << 1)
+#define DUMP_COMPONENT_COMMENT		(1 << 2)
+#define DUMP_COMPONENT_SECLABEL		(1 << 3)
+#define DUMP_COMPONENT_ACL			(1 << 4)
+#define DUMP_COMPONENT_POLICY		(1 << 5)
+#define DUMP_COMPONENT_USERMAP		(1 << 6)
+#define DUMP_COMPONENT_ALL			(0xFFFF)
+
 typedef struct _dumpableObject
 {
 	DumpableObjectType objType;
@@ -86,7 +98,7 @@ typedef struct _dumpableObject
 	DumpId		dumpId;			/* assigned by AssignDumpId() */
 	char	   *name;			/* object name (should never be NULL) */
 	struct _namespaceInfo *namespace;	/* containing namespace, or NULL */
-	bool		dump;			/* true if we want to dump this object */
+	DumpComponents dump;	/* bitmask of components to dump */
 	bool		ext_member;		/* true if object is member of extension */
 	DumpId	   *dependencies;	/* dumpIds of objects this one depends on */
 	int			nDeps;			/* number of valid dependencies */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9caa096..150902e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2758,8 +2758,12 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
+DATA(insert OID = 3286 (  pg_stat_get_activity_all			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity_all _null_ _null_ _null_ ));
+DESCR("statistics: information about currently active backends, unfiltered");
 DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
+DATA(insert OID = 3285 (  pg_stat_get_wal_senders_all	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders_all _null_ _null_ _null_ ));
+DESCR("statistics: information about currently active replication, unfiltered");
 DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
 DESCR("statistics: current backend PID");
 DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
@@ -3109,6 +3113,9 @@ DATA(insert OID = 2171 ( pg_cancel_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v
 DESCR("cancel a server process' current query");
 DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
 DESCR("terminate a server process");
+DATA(insert OID = 3284 ( pg_signal_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "23 23" _null_ _null_ _null_ _null_ pg_signal_backend _null_ _null_ _null_ ));
+DESCR("signal a server process");
+
 DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 3220 "25 16" _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
 DESCR("prepare for taking an online backup");
 DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
index b10e784..e458621 100644
--- a/src/include/replication/walsender.h
+++ b/src/include/replication/walsender.h
@@ -37,6 +37,7 @@ extern void WalSndWakeup(void);
 extern void WalSndRqstFileReload(void);
 
 extern Datum pg_stat_get_wal_senders(PG_FUNCTION_ARGS);
+extern Datum pg_stat_get_wal_senders_all(PG_FUNCTION_ARGS);
 
 /*
  * Remember that we want to wakeup walsenders later
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 33a453f..a126a76 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -482,6 +482,7 @@ extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
 extern Datum current_database(PG_FUNCTION_ARGS);
 extern Datum current_query(PG_FUNCTION_ARGS);
 extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+extern Datum pg_signal_backend(PG_FUNCTION_ARGS);
 extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
 extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
 extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 71fa44a..8ec7fa9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1636,6 +1636,28 @@ pg_stat_activity| SELECT s.datid,
     pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
     pg_authid u
   WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
+pg_stat_activity_all| SELECT s.datid,
+    d.datname,
+    s.pid,
+    s.usesysid,
+    u.rolname AS usename,
+    s.application_name,
+    s.client_addr,
+    s.client_hostname,
+    s.client_port,
+    s.backend_start,
+    s.xact_start,
+    s.query_start,
+    s.state_change,
+    s.waiting,
+    s.state,
+    s.backend_xid,
+    s.backend_xmin,
+    s.query
+   FROM pg_database d,
+    pg_stat_get_activity_all(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
+    pg_authid u
+  WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
 pg_stat_all_indexes| SELECT c.oid AS relid,
     i.oid AS indexrelid,
     n.nspname AS schemaname,
@@ -1743,6 +1765,26 @@ pg_stat_replication| SELECT s.pid,
     pg_authid u,
     pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
   WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+pg_stat_replication_all| SELECT s.pid,
+    s.usesysid,
+    u.rolname AS usename,
+    s.application_name,
+    s.client_addr,
+    s.client_hostname,
+    s.client_port,
+    s.backend_start,
+    s.backend_xmin,
+    w.state,
+    w.sent_location,
+    w.write_location,
+    w.flush_location,
+    w.replay_location,
+    w.sync_priority,
+    w.sync_state
+   FROM pg_stat_get_activity_all(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
+    pg_authid u,
+    pg_stat_get_wal_senders_all() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
+  WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
 pg_stat_ssl| SELECT s.pid,
     s.ssl,
     s.sslversion AS version,
-- 
1.9.1

#107Stephen Frost
sfrost@snowman.net
In reply to: Stephen Frost (#106)
1 attachment(s)
Re: Additional role attributes && superuser review

Robert, all,

* Stephen Frost (sfrost@snowman.net) wrote:

* Stephen Frost (sfrost@snowman.net) wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

The tricky part of this seems to me to be the pg_dump changes. The
new catalog flag seems a little sketchy to me; wouldn't it be better
to make the dump flag into an enum, DUMP_EVERYTHING, DUMP_ACL,
DUMP_NONE?

I agree that the pg_dump changes are a very big part of this change.
I'll look at using an enum and see if that would work.

I've implemented this approach and there are things which I like about
it and things which I don't. I'd love to hear your thoughts. As
mentioned previously, this patch does not break the pg_stat_activity or
pg_stat_replication view APIs. Similairly, the functions which directly
feed those views return the same results they did previously, there are
just additional functions now which provide everything, and view on top
of those, for users who are GRANT'd access to them.

Here is the latest revision of this patch.

The big change here is the addition of default roles. This approach has
been brought up a few times and Magnus recently mentioned it to me
again. Having the default roles greatly reduced the impact of this
change on the test_deparse regression test, which was quite nice.

Updates are included for pg_upgrade and pg_dumpall to handle roles which
start with "pg_" specially as we are now claiming those as "System
defined" roles (similar to how we claim schemas starting with pg_ are
system defined, etc). These new default roles are in line with the
previously discussed role attributes, but have the advantage that they
act just like normal roles and work inside of the normal permissions
system. They are:

pg_backup - Start and stop backups, switch xlogs, create restore points.
pg_monitor - View privileged system info (user activity, replica lag)
pg_replay - Control XLOG replay on replica (pause, resume)
pg_replication - Create, destroy, work with replication slots

pg_admin - All of the above, plus rotate logfiles and signal backends

Feedback on all of this would be great. One interesting idea is that,
with these defined default roles, we could rip out the majority of the
changes to pg_dump and declare that users have to use only the roles we
provide to manage access to those functions (or risk any changes they
make to the ACLs of system objects disappearing across upgrades or
pg_dump/restore's, which is what happens today anyway). I'm a bit on
the fence about it myself; it'd certainly reduce the risk of this change
but it would also limit users to only being able to operate at the
pre-defined levels we've set, but then again, the same was going to be
true with the role attributes-based approach and I don't recall any
complaints during that discussion.

Thoughts? Feedback on this would be most welcome; it's been a long time
incubating and I'd really like to get this capability in and close it
out of the current commitfest. I'm certainly of the opinion that it
will be a welcome step forward for quite a few of our users as the
discussion about how to create non-superuser roles for certain
operations (a monitor role, in particular, but also backup and replay)
has come up quite a bit, both on the lists and directly from clients.

Thanks!

Stephen

Attachments:

catalog_function_acls_v4.patchtext/x-diff; charset=us-asciiDownload
From 488df9c567fdd0a56afa084a8f22f8b8a2412bd7 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Thu, 19 Mar 2015 14:49:26 -0400
Subject: [PATCH] Use GRANT for access to privileged functions

Historically, we have hard-coded the permissions for privileged
functions in C code (eg: if (superuser()) then allow X, else don't).
Unfortunately, that's pretty limiting and means that functions which are
useful for roles that should not be superusers (eg: monitoring, backup
management) require that the user calling them be a superuser.  This
leads to far more uses of superuser roles than is ideal.

Thankfully, we have a very handy and complex privilege system for
managing who has access to what already built into PG.  This is the
GRANT system which has existed since very near the beginning of PG.

This provides a set of system functions which are not able to be
executed by all users by default and allows administrators to grant
access to those functions for the users (eg: monitoring or other roles)
where they feel it is appropriate.

Further, create a set of pre-defined roles (which start with "pg_")
for administrators to use to grant bulk access with.  This greatly
simplifies the granting of "monitor", "backup", and similar privileges.
pg_upgrade and pg_dumpall are updated to treat roles starting with
"pg_" in much the same way as the default role is handled, and
pg_upgrade has been taught to complain if it finds any roles starting
with "pg_" in a 9.4 or older system.  Having pre-defined roles also
allows 3rd-party utilities (eg: check_postgres.pl) to depend on these
roles and the access they provide in their documentation for users
configuring those systems.

We avoid breaking existing APIs for the existing system views by
providing backwards compatible functions which continue to filter in
the C code based on if the caller is a superuser or not.

For a few functions (eg: pg_logical_slot_* and friends), it makes more
sense to break compatibility, as they are relatively new, and require
admins to GRANT access to those functions (or the "pg_replication" role
which has been created) explicitly on upgrade (this should be noted in
the release notes).  Note that replication connections do not go
through the normal GRANT system and therefore tools such as
pg_basebackup will not be impacted by this change.  In general, this
change requires administrators to be more explicit about which roles
have access to these capabilities, which is a net improvement.

Last, but certainly not least, this changes pg_dump to include ACLs for
objects in the pg_catalog schema, where the ACL is not the default for
the object type (which is the case for most non-function objects in
pg_catalog).  This is necessary as administrators are now allowed to
set privileges on those objects differently from their initdb-set
values and we need to preserve those settings.  Revoking catalog access
does not hide objects from CREATE and similar commands, of course,
meaning that this is not a solution for multi-tenancy, but that is not
the goal of this change.
---
 contrib/test_decoding/expected/permissions.out |    9 +-
 contrib/test_decoding/sql/permissions.sql      |    2 +
 doc/src/sgml/user-manag.sgml                   |   69 ++
 src/backend/access/transam/xlogfuncs.c         |   30 -
 src/backend/catalog/system_views.sql           |  133 +++
 src/backend/replication/logical/logicalfuncs.c |   11 -
 src/backend/replication/slotfuncs.c            |   15 -
 src/backend/replication/walsender.c            |   86 +-
 src/backend/utils/adt/misc.c                   |   64 +-
 src/backend/utils/adt/pgstatfuncs.c            |  246 +++--
 src/bin/pg_dump/pg_dump.c                      | 1389 +++++++++++++-----------
 src/bin/pg_dump/pg_dump.h                      |   14 +-
 src/bin/pg_dump/pg_dumpall.c                   |    6 +-
 src/bin/pg_upgrade/check.c                     |   40 +-
 src/include/catalog/pg_proc.h                  |    6 +
 src/include/replication/walsender.h            |    1 +
 src/include/utils/builtins.h                   |    1 +
 src/test/regress/expected/rules.out            |   42 +
 18 files changed, 1318 insertions(+), 846 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..68dd7b1 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -4,6 +4,7 @@ SET synchronous_commit = on;
 CREATE ROLE lr_normal;
 CREATE ROLE lr_superuser SUPERUSER;
 CREATE ROLE lr_replication REPLICATION;
+GRANT pg_replication TO lr_replication;
 CREATE TABLE lr_test(data text);
 -- superuser can control replication
 SET ROLE lr_superuser;
@@ -54,13 +55,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_create_logical_replication_slot
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_logical_slot_get_changes
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_drop_replication_slot
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +91,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  permission denied for function pg_drop_replication_slot
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/contrib/test_decoding/sql/permissions.sql b/contrib/test_decoding/sql/permissions.sql
index 8680c55..fad5a09 100644
--- a/contrib/test_decoding/sql/permissions.sql
+++ b/contrib/test_decoding/sql/permissions.sql
@@ -5,6 +5,8 @@ SET synchronous_commit = on;
 CREATE ROLE lr_normal;
 CREATE ROLE lr_superuser SUPERUSER;
 CREATE ROLE lr_replication REPLICATION;
+GRANT pg_replication TO lr_replication;
+
 CREATE TABLE lr_test(data text);
 
 -- superuser can control replication
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 177ac7a..7e21e15 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -414,6 +414,75 @@ DROP ROLE <replaceable>name</replaceable>;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, capabilities and
+   information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, instead of having to GRANT rights
+   at the individual object level.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch xlogs, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_montior</entry>
+       <entry>To privileged system information (eg: activity of other users, replication lag)</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume xlog replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_admin</entry>
+       <entry>Granted pg_backup, pg_monitor, pg_reply, pg_replication, roles, and allowed to rotate logfiles, and signal other backends.</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+GRANT pg_admin TO admin_user;
+</programlisting>
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2179bf7..c9d9f3d 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -54,11 +54,6 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	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);
 
 	PG_RETURN_LSN(startpoint);
@@ -82,11 +77,6 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	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);
 
 	PG_RETURN_LSN(stoppoint);
@@ -100,11 +90,6 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
-
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -129,11 +114,6 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	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,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -338,11 +318,6 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
-
 	if (!RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -360,11 +335,6 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
-
 	if (!RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 4c35ef4..8b70604 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -623,6 +623,32 @@ CREATE VIEW pg_stat_activity AS
     WHERE S.datid = D.oid AND
             S.usesysid = U.oid;
 
+CREATE VIEW pg_stat_activity_all AS
+    SELECT
+            S.datid AS datid,
+            D.datname AS datname,
+            S.pid,
+            S.usesysid,
+            U.rolname AS usename,
+            S.application_name,
+            S.client_addr,
+            S.client_hostname,
+            S.client_port,
+            S.backend_start,
+            S.xact_start,
+            S.query_start,
+            S.state_change,
+            S.waiting,
+            S.state,
+            S.backend_xid,
+            s.backend_xmin,
+            S.query
+    FROM pg_database D, pg_stat_get_activity_all(NULL) AS S, pg_authid U
+    WHERE S.datid = D.oid AND
+            S.usesysid = U.oid;
+
+REVOKE ALL on pg_stat_activity_all FROM public;
+
 CREATE VIEW pg_stat_replication AS
     SELECT
             S.pid,
@@ -657,6 +683,31 @@ CREATE VIEW pg_stat_ssl AS
             S.sslclientdn AS clientdn
     FROM pg_stat_get_activity(NULL) AS S;
 
+CREATE VIEW pg_stat_replication_all AS
+    SELECT
+            S.pid,
+            S.usesysid,
+            U.rolname AS usename,
+            S.application_name,
+            S.client_addr,
+            S.client_hostname,
+            S.client_port,
+            S.backend_start,
+            S.backend_xmin,
+            W.state,
+            W.sent_location,
+            W.write_location,
+            W.flush_location,
+            W.replay_location,
+            W.sync_priority,
+            W.sync_state
+    FROM pg_stat_get_activity_all(NULL) AS S, pg_authid U,
+            pg_stat_get_wal_senders_all() AS W
+    WHERE S.usesysid = U.oid AND
+            S.pid = W.pid;
+
+REVOKE ALL on pg_stat_replication_all FROM public;
+
 CREATE VIEW pg_replication_slots AS
     SELECT
             L.slot_name,
@@ -909,3 +960,85 @@ RETURNS interval
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'make_interval';
+
+-- Revoke privileges for functions that should not be available to
+-- all users.  Administrators are allowed to change this later, if
+-- they wish.
+
+-- XLOG location can leak information based on compressed WAL records
+REVOKE EXECUTE ON FUNCTION pg_current_xlog_insert_location() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_last_xlog_receive_location() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_current_xlog_location() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_last_xlog_replay_location() FROM public;
+
+-- Unfiltered information about activity on the system
+REVOKE EXECUTE ON FUNCTION pg_stat_get_activity_all(integer) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stat_get_wal_senders_all() FROM public;
+
+-- Monitoring user needs to be able to see XLOG replay info
+-- Also allowed to view all user activity
+CREATE ROLE pg_monitor;
+GRANT EXECUTE ON FUNCTION pg_current_xlog_insert_location() TO pg_monitor;
+GRANT EXECUTE ON FUNCTION pg_last_xlog_receive_location() TO pg_monitor;
+GRANT EXECUTE ON FUNCTION pg_current_xlog_location() TO pg_monitor;
+GRANT EXECUTE ON FUNCTION pg_last_xlog_replay_location() TO pg_monitor;
+GRANT EXECUTE ON FUNCTION pg_stat_get_activity_all(integer) TO pg_monitor;
+GRANT EXECUTE ON FUNCTION pg_stat_get_wal_senders_all() TO pg_monitor;
+GRANT SELECT ON TABLE pg_stat_activity_all TO pg_monitor;
+GRANT SELECT ON TABLE pg_stat_replication_all TO pg_monitor;
+
+-- Starting/stopping backups are not appropriate for normal users
+REVOKE EXECUTE ON FUNCTION pg_start_backup(text, boolean) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_stop_backup() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_switch_xlog() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_create_restore_point(text) FROM public;
+
+-- pg_backup role is created to allow access to these functions
+CREATE ROLE pg_backup;
+GRANT EXECUTE ON FUNCTION pg_start_backup(text, boolean) TO pg_backup;
+GRANT EXECUTE ON FUNCTION pg_stop_backup() TO pg_backup;
+GRANT EXECUTE ON FUNCTION pg_switch_xlog() TO pg_backup;
+GRANT EXECUTE ON FUNCTION pg_create_restore_point(text) TO pg_backup;
+
+-- Only certain users should be able to rotate logfiles
+REVOKE EXECUTE ON FUNCTION pg_rotate_logfile() FROM public;
+
+-- Only privileged users should be able to signal other backends
+REVOKE EXECUTE ON FUNCTION pg_signal_backend(int, int) FROM public;
+
+-- Replica control
+REVOKE EXECUTE ON FUNCTION pg_xlog_replay_pause() FROM public;
+REVOKE EXECUTE ON FUNCTION pg_xlog_replay_resume() FROM public;
+
+-- Create pg_replay role for controlling replica replay
+CREATE ROLE pg_replay;
+GRANT EXECUTE ON FUNCTION pg_xlog_replay_pause() TO pg_replay;
+GRANT EXECUTE ON FUNCTION pg_xlog_replay_resume() TO pg_replay;
+
+-- Creating and working with replication slots should not be available
+-- to all users.
+REVOKE EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_create_logical_replication_slot(name, name) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_drop_replication_slot(name) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+REVOKE EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[]) FROM public;
+
+-- Role which is allowed to work with replication slots
+CREATE ROLE pg_replication;
+GRANT EXECUTE ON FUNCTION pg_create_physical_replication_slot(name) TO pg_replication;
+GRANT EXECUTE ON FUNCTION pg_drop_replication_slot(name) TO pg_replication;
+GRANT EXECUTE ON FUNCTION pg_create_logical_replication_slot(name,name) TO pg_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_get_changes(name, pg_lsn, int, VARIADIC options text[])TO pg_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_changes(name, pg_lsn, int, VARIADIC options text[])TO pg_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_get_binary_changes(name, pg_lsn, int, VARIADIC options text[])TO pg_replication;
+GRANT EXECUTE ON FUNCTION pg_logical_slot_peek_binary_changes(name, pg_lsn, int, VARIADIC options text[])TO pg_replication;
+
+-- Admin user allowed to rotate logfiles, signal backends
+-- and gets monitor, backup, replay, and replication
+CREATE ROLE pg_admin;
+GRANT pg_monitor, pg_backup, pg_replay, pg_replication TO pg_admin;
+GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO pg_admin;
+GRANT EXECUTE ON FUNCTION pg_signal_backend(int, int) TO pg_admin;
+
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 3be5263..2995bfa 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -202,15 +202,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,8 +315,6 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
-
 	CheckLogicalDecodingRequirements();
 
 	arr = PG_GETARG_ARRAYTYPE_P(3);
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 3d9aadb..4cdc78d 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -23,15 +23,6 @@
 #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.
@@ -51,8 +42,6 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
-
 	CheckSlotRequirements();
 
 	/* acquire replication slot, this will check for conflicting names */
@@ -94,8 +83,6 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
-
 	CheckLogicalDecodingRequirements();
 
 	/*
@@ -143,8 +130,6 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
-
 	CheckSlotRequirements();
 
 	ReplicationSlotDrop(NameStr(*name));
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4a20569..fb16eef 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -217,6 +217,7 @@ static XLogRecPtr WalSndWaitForWal(XLogRecPtr loc);
 
 static void XLogRead(char *buf, XLogRecPtr startptr, Size count);
 
+static void populate_pg_stat_get_wal_senders(TupleDesc tupdesc, Tuplestorestate *tupstore, bool filter);
 
 /* Initialize walsender process before entering the main command loop */
 void
@@ -2717,22 +2718,21 @@ WalSndGetStateString(WalSndState state)
 	return "UNKNOWN";
 }
 
+#define PG_STAT_GET_WAL_SENDERS_COLS	8
 
 /*
  * Returns activity of walsenders, including pids and xlog locations sent to
- * standby servers.
+ * standby servers.  Note that this version filters out the results unless the
+ * caller is a superuser.
  */
 Datum
 pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_WAL_SENDERS_COLS	8
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	TupleDesc	tupdesc;
 	Tuplestorestate *tupstore;
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
-	WalSnd	   *sync_standby;
-	int			i;
 
 	/* check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
@@ -2760,6 +2760,73 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 	MemoryContextSwitchTo(oldcontext);
 
 	/*
+	 * Populate the tuplestore.
+	 *
+	 * For non-superusers, we ask that the results be filtered.
+	 */
+	populate_pg_stat_get_wal_senders(tupdesc, tupstore, !superuser());
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
+ * Returns activity of walsenders, including pids and xlog locations sent to
+ * standby servers.  Note that this version does NOT filter out the results,
+ * therefore the permissions must be managed at the GRANT level.
+ */
+Datum
+pg_stat_get_wal_senders_all(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Populate the tuplestore */
+	populate_pg_stat_get_wal_senders(tupdesc, tupstore, false);
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+static void
+populate_pg_stat_get_wal_senders(TupleDesc tupdesc, Tuplestorestate *tupstore, bool filter)
+{
+	WalSnd	   *sync_standby;
+	int			i;
+
+	/*
 	 * Get the currently active synchronous standby.
 	 */
 	LWLockAcquire(SyncRepLock, LW_SHARED);
@@ -2794,11 +2861,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (filter)
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * When asked to filter record results, set all the rest of the
+			 * columns to NULL.
 			 */
 			MemSet(&nulls[1], true, PG_STAT_GET_WAL_SENDERS_COLS - 1);
 		}
@@ -2843,10 +2910,7 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
 
-	/* clean up and return the tuplestore */
-	tuplestore_donestoring(tupstore);
-
-	return (Datum) 0;
+	return;
 }
 
 /*
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 61d609f..0204da6 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -76,11 +76,19 @@ current_query(PG_FUNCTION_ARGS)
 }
 
 /*
- * Send a signal to another backend.
+ * Internal helper function for sending a signal to another backend.
  *
- * The signal is delivered if the user is either a superuser or the same
- * role as the backend being signaled. For "dangerous" signals, an explicit
- * check for superuser needs to be done prior to calling this function.
+ * The signal is delivered if the user is a superuser.  If the other backend
+ * is owned by a superuser role, then the calling user must be a superuser.
+ *
+ * When perm_check is passed in as true, then the user must be a member of
+ * the role which owns the backend being signaled.  For "dangerous" signals,
+ * an explicit check for superuser needs to be done prior to calling this
+ * function.
+ *
+ * When perm_check is passwd in as false, then no check of role membership is
+ * performed as the GRANT system is expected to have been used to manage access
+ * to calling the function which called us.
  *
  * Returns 0 on success, 1 on general failure, 2 on normal permission error
  * and 3 if the caller needs to be a superuser.
@@ -94,7 +102,7 @@ current_query(PG_FUNCTION_ARGS)
 #define SIGNAL_BACKEND_NOPERMISSION 2
 #define SIGNAL_BACKEND_NOSUPERUSER 3
 static int
-pg_signal_backend(int pid, int sig)
+pg_signal_backend_helper(int pid, int sig, bool perm_check)
 {
 	PGPROC	   *proc = BackendPidGetProc(pid);
 
@@ -122,7 +130,7 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (perm_check && !has_privs_of_role(GetUserId(), proc->roleId))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -150,6 +158,41 @@ pg_signal_backend(int pid, int sig)
 }
 
 /*
+ * Signal a backend process.  Permissions for this are managed by the GRANT
+ * system and therefore we do not do any extra permissions checks through
+ * this path.
+ *
+ * Note that only superusers can signal superuser-owned processes.
+ */
+Datum
+pg_signal_backend(PG_FUNCTION_ARGS)
+{
+	int			backend = PG_GETARG_INT32(0);
+	int			signal = PG_GETARG_INT32(1);
+	int			r;
+
+	/*
+	 * We only allow "safe" signals to be used through this, unless the user
+	 * is a superuser.
+	 */
+	if (!superuser() && signal != SIGINT && signal != SIGTERM)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be a superuser to send signals other than SIGINT and SIGTERM"))));
+
+	r = pg_signal_backend_helper(backend, signal, false);
+
+	if (r == SIGNAL_BACKEND_NOSUPERUSER)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be a superuser to cancel superuser query"))));
+
+	Assert (r != SIGNAL_BACKEND_NOPERMISSION);
+
+	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
+}
+
+/*
  * Signal to cancel a backend process.  This is allowed if you are a member of
  * the role whose process is being canceled.
  *
@@ -158,7 +201,7 @@ pg_signal_backend(int pid, int sig)
 Datum
 pg_cancel_backend(PG_FUNCTION_ARGS)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
+	int			r = pg_signal_backend_helper(PG_GETARG_INT32(0), SIGINT, true);
 
 	if (r == SIGNAL_BACKEND_NOSUPERUSER)
 		ereport(ERROR,
@@ -182,7 +225,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 Datum
 pg_terminate_backend(PG_FUNCTION_ARGS)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
+	int			r = pg_signal_backend_helper(PG_GETARG_INT32(0), SIGTERM, true);
 
 	if (r == SIGNAL_BACKEND_NOSUPERUSER)
 		ereport(ERROR,
@@ -225,11 +268,6 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 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)
 	{
 		ereport(WARNING,
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index bbe94c3..ea9a9f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -53,6 +53,7 @@ extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
+extern Datum pg_stat_get_activity_all(PG_FUNCTION_ARGS);
 extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
@@ -126,6 +127,8 @@ extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS);
 /* Global bgwriter statistics, from bgwriter.c */
 extern PgStat_MsgBgWriter bgwriterStats;
 
+static void populate_pg_stat_get_activity(TupleDesc tupdesc, Tuplestorestate *tupstore, int pid, Oid calling_user);
+
 Datum
 pg_stat_get_numscans(PG_FUNCTION_ARGS)
 {
@@ -524,137 +527,146 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
 	}
 }
 
+/*
+ * Returns activity of other backends.  Note that this version filters out
+ * the results unless the caller is a member of the role of the backend being
+ * examined.
+ */
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc   tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
 
-	if (SRF_IS_FIRSTCALL())
-	{
-		MemoryContext oldcontext;
-		TupleDesc	tupdesc;
+	/*
+	 * Populate the tuplestore.
+	 *
+	 * For this path, we pass in the current GetUserId() result and have the
+	 * populate function filter the results based on that.
+	 */
+	populate_pg_stat_get_activity(tupdesc, tupstore, PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0), GetUserId());
 
-		funcctx = SRF_FIRSTCALL_INIT();
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
 
-		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-		tupdesc = CreateTemplateTupleDesc(22, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
-						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
-						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid",
-						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "state",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "query",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "waiting",
-						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "act_start",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "query_start",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "backend_start",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "state_change",
-						   TIMESTAMPTZOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_addr",
-						   INETOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 13, "client_hostname",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
-						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 15, "backend_xid",
-						   XIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
-						   XIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 17, "ssl",
-						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 18, "sslversion",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 19, "sslcipher",
-						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 20, "sslbits",
-						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 21, "sslcompression",
-						   BOOLOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 22, "sslclientdn",
-						   TEXTOID, -1, 0);
-
-		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
-
-		funcctx->user_fctx = palloc0(sizeof(int));
-		if (PG_ARGISNULL(0))
-		{
-			/* Get all backends */
-			funcctx->max_calls = pgstat_fetch_stat_numbackends();
-		}
-		else
-		{
-			/*
-			 * Get one backend - locate by pid.
-			 *
-			 * We lookup the backend early, so we can return zero rows if it
-			 * doesn't exist, instead of returning a single row full of NULLs.
-			 */
-			int			pid = PG_GETARG_INT32(0);
-			int			i;
-			int			n = pgstat_fetch_stat_numbackends();
+	return (Datum) 0;
+}
 
-			for (i = 1; i <= n; i++)
-			{
-				PgBackendStatus *be = pgstat_fetch_stat_beentry(i);
+/*
+ * Returns activity of other backends.  Note that this version does NOT
+ * filter the results and therefore the permissions at the SQL level must
+ * be REVOKE'd from public.
+ */
+Datum
+pg_stat_get_activity_all(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc   tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
 
-				if (be)
-				{
-					if (be->st_procpid == pid)
-					{
-						*(int *) (funcctx->user_fctx) = i;
-						break;
-					}
-				}
-			}
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
 
-			if (*(int *) (funcctx->user_fctx) == 0)
-				/* Pid not found, return zero rows */
-				funcctx->max_calls = 0;
-			else
-				funcctx->max_calls = 1;
-		}
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
 
-		MemoryContextSwitchTo(oldcontext);
-	}
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
 
-	/* stuff done on every call of the function */
-	funcctx = SRF_PERCALL_SETUP();
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Populate the tuplestore.
+	 *
+	 * For this path, we pass in the current GetUserId() result and have the
+	 * populate function filter the results based on that.
+	 */
+	populate_pg_stat_get_activity(tupdesc, tupstore, PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0), InvalidOid);
 
-	if (funcctx->call_cntr < funcctx->max_calls)
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+static void
+populate_pg_stat_get_activity(TupleDesc tupdesc, Tuplestorestate *tupstore, int pid, Oid calling_user)
+{
+	int num_backends = pgstat_fetch_stat_numbackends();
+	int curr_backend;
+
+	/* 1-based index */
+	for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
 	{
 		/* for each row */
 		Datum		values[22];
 		bool		nulls[22];
-		HeapTuple	tuple;
 		LocalPgBackendStatus *local_beentry;
 		PgBackendStatus *beentry;
 
 		MemSet(values, 0, sizeof(values));
 		MemSet(nulls, 0, sizeof(nulls));
 
-		if (*(int *) (funcctx->user_fctx) > 0)
+		if (pid != -1)
 		{
-			/* Get specific pid slot */
-			local_beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx));
-			beentry = &local_beentry->backendStatus;
-		}
-		else
-		{
-			/* Get the next one in the list */
-			local_beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1);	/* 1-based index */
-			beentry = &local_beentry->backendStatus;
+			/* Skip any which are not the one we're looking for. */
+			PgBackendStatus *be = pgstat_fetch_stat_beentry(curr_backend);
+
+			if (!be || be->st_procpid != pid)
+				continue;
+
 		}
+
+		/* Get the next one in the list */
+		local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+		if (!local_beentry)
+			continue;
+
+		beentry = &local_beentry->backendStatus;
 		if (!beentry)
 		{
 			int			i;
@@ -665,8 +677,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[5] = false;
 			values[5] = CStringGetTextDatum("<backend information not available>");
 
-			tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-			SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+			continue;
 		}
 
 		/* Values available to all callers */
@@ -704,7 +716,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (calling_user == InvalidOid ||
+			has_privs_of_role(calling_user, beentry->st_userid))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -839,15 +852,14 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[13] = true;
 		}
 
-		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
-	}
-	else
-	{
-		/* nothing left */
-		SRF_RETURN_DONE(funcctx);
+		/* If only a single backend was requested, and we found it, break. */
+		if (pid != -1)
+			break;
 	}
+
+	return;
 }
 
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 977b72e..b8815d4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1236,16 +1236,20 @@ selectDumpableNamespace(NamespaceInfo *nsinfo)
 	 * namespaces. If specific namespaces are being dumped, dump just those
 	 * namespaces. Otherwise, dump all non-system namespaces.
 	 */
+
 	if (table_include_oids.head != NULL)
-		nsinfo->dobj.dump = false;
+		nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else if (schema_include_oids.head != NULL)
 		nsinfo->dobj.dump = simple_oid_list_member(&schema_include_oids,
-												   nsinfo->dobj.catId.oid);
+												   nsinfo->dobj.catId.oid) ?
+							DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
+	else if (strncmp(nsinfo->dobj.name, "pg_catalog", 10) == 0)
+		nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
 	else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
 			 strcmp(nsinfo->dobj.name, "information_schema") == 0)
-		nsinfo->dobj.dump = false;
+		nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		nsinfo->dobj.dump = true;
+		nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
 
 	/*
 	 * In any case, a namespace can be excluded by an exclusion switch
@@ -1253,7 +1257,7 @@ selectDumpableNamespace(NamespaceInfo *nsinfo)
 	if (nsinfo->dobj.dump &&
 		simple_oid_list_member(&schema_exclude_oids,
 							   nsinfo->dobj.catId.oid))
-		nsinfo->dobj.dump = false;
+		nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1269,7 +1273,8 @@ selectDumpableTable(TableInfo *tbinfo)
 	 */
 	if (table_include_oids.head != NULL)
 		tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
-												   tbinfo->dobj.catId.oid);
+												   tbinfo->dobj.catId.oid) ?
+							DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 	else
 		tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump;
 
@@ -1279,7 +1284,7 @@ selectDumpableTable(TableInfo *tbinfo)
 	if (tbinfo->dobj.dump &&
 		simple_oid_list_member(&table_exclude_oids,
 							   tbinfo->dobj.catId.oid))
-		tbinfo->dobj.dump = false;
+		tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1308,7 +1313,7 @@ selectDumpableType(TypeInfo *tyinfo)
 		if (tytable != NULL)
 			tyinfo->dobj.dump = tytable->dobj.dump;
 		else
-			tyinfo->dobj.dump = false;
+			tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
 		return;
 	}
 
@@ -1325,16 +1330,11 @@ selectDumpableType(TypeInfo *tyinfo)
 		 */
 	}
 
-	/* dump only types in dumpable namespaces */
-	if (!tyinfo->dobj.namespace->dobj.dump)
-		tyinfo->dobj.dump = false;
-
 	/* skip undefined placeholder types */
-	else if (!tyinfo->isDefined)
-		tyinfo->dobj.dump = false;
-
+	if (!tyinfo->isDefined)
+		tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		tyinfo->dobj.dump = true;
+		tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump;
 }
 
 /*
@@ -1351,7 +1351,7 @@ selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
 	if (dinfo->dobj.namespace)
 		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
 	else
-		dinfo->dobj.dump = dopt->include_everything;
+		dinfo->dobj.dump = dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1367,9 +1367,9 @@ static void
 selectDumpableCast(DumpOptions *dopt, CastInfo *cast)
 {
 	if (cast->dobj.catId.oid < (Oid) FirstNormalObjectId)
-		cast->dobj.dump = false;
+		cast->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		cast->dobj.dump = dopt->include_everything;
+		cast->dobj.dump = dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1386,9 +1386,9 @@ static void
 selectDumpableExtension(DumpOptions *dopt, ExtensionInfo *extinfo)
 {
 	if (dopt->binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
-		extinfo->dobj.dump = false;
+		extinfo->dobj.dump = DUMP_COMPONENT_NONE;
 	else
-		extinfo->dobj.dump = dopt->include_everything;
+		extinfo->dobj.dump = dopt->include_everything ? DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
 }
 
 /*
@@ -1810,13 +1810,14 @@ dumpTableData(Archive *fout, DumpOptions *dopt, TableDataInfo *tdinfo)
 	 * dependency on its table as "special" and pass it to ArchiveEntry now.
 	 * See comments for BuildArchiveDependencies.
 	 */
-	ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
-				 tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name,
-				 NULL, tbinfo->rolname,
-				 false, "TABLE DATA", SECTION_DATA,
-				 "", "", copyStmt,
-				 &(tbinfo->dobj.dumpId), 1,
-				 dumpFn, tdinfo);
+	if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
+		ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
+					 tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name,
+					 NULL, tbinfo->rolname,
+					 false, "TABLE DATA", SECTION_DATA,
+					 "", "", copyStmt,
+					 &(tbinfo->dobj.dumpId), 1,
+					 dumpFn, tdinfo);
 
 	destroyPQExpBuffer(copyBuf);
 	destroyPQExpBuffer(clistBuf);
@@ -1844,23 +1845,24 @@ refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
 	appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
 					  fmtId(tbinfo->dobj.name));
 
-	ArchiveEntry(fout,
-				 tdinfo->dobj.catId,	/* catalog ID */
-				 tdinfo->dobj.dumpId,	/* dump ID */
-				 tbinfo->dobj.name,		/* Name */
-				 tbinfo->dobj.namespace->dobj.name,		/* Namespace */
-				 NULL,			/* Tablespace */
-				 tbinfo->rolname,		/* Owner */
-				 false,			/* with oids */
-				 "MATERIALIZED VIEW DATA",		/* Desc */
-				 SECTION_POST_DATA,		/* Section */
-				 q->data,		/* Create */
-				 "",			/* Del */
-				 NULL,			/* Copy */
-				 tdinfo->dobj.dependencies,		/* Deps */
-				 tdinfo->dobj.nDeps,	/* # Deps */
-				 NULL,			/* Dumper */
-				 NULL);			/* Dumper Arg */
+	if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
+		ArchiveEntry(fout,
+					 tdinfo->dobj.catId,	/* catalog ID */
+					 tdinfo->dobj.dumpId,	/* dump ID */
+					 tbinfo->dobj.name,		/* Name */
+					 tbinfo->dobj.namespace->dobj.name,		/* Namespace */
+					 NULL,			/* Tablespace */
+					 tbinfo->rolname,		/* Owner */
+					 false,			/* with oids */
+					 "MATERIALIZED VIEW DATA",		/* Desc */
+					 SECTION_POST_DATA,		/* Section */
+					 q->data,		/* Create */
+					 "",			/* Del */
+					 NULL,			/* Copy */
+					 tdinfo->dobj.dependencies,		/* Deps */
+					 tdinfo->dobj.nDeps,	/* # Deps */
+					 NULL,			/* Dumper */
+					 NULL);			/* Dumper Arg */
 
 	destroyPQExpBuffer(q);
 }
@@ -1876,7 +1878,7 @@ getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, bool oids)
 
 	for (i = 0; i < numTables; i++)
 	{
-		if (tblinfo[i].dobj.dump)
+		if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA)
 			makeTableDataInfo(dopt, &(tblinfo[i]), oids);
 	}
 }
@@ -2686,31 +2688,38 @@ dumpBlob(Archive *fout, DumpOptions *dopt, BlobInfo *binfo)
 					  "SELECT pg_catalog.lo_unlink('%s');\n",
 					  binfo->dobj.name);
 
-	ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId,
-				 binfo->dobj.name,
-				 NULL, NULL,
-				 binfo->rolname, false,
-				 "BLOB", SECTION_PRE_DATA,
-				 cquery->data, dquery->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (binfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId,
+					 binfo->dobj.name,
+					 NULL, NULL,
+					 binfo->rolname, false,
+					 "BLOB", SECTION_PRE_DATA,
+					 cquery->data, dquery->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* set up tag for comment and/or ACL */
 	resetPQExpBuffer(cquery);
 	appendPQExpBuffer(cquery, "LARGE OBJECT %s", binfo->dobj.name);
 
 	/* Dump comment if any */
-	dumpComment(fout, dopt, cquery->data,
-				NULL, binfo->rolname,
-				binfo->dobj.catId, 0, binfo->dobj.dumpId);
+	if (binfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, cquery->data,
+					NULL, binfo->rolname,
+					binfo->dobj.catId, 0, binfo->dobj.dumpId);
 
 	/* Dump security label if any */
-	dumpSecLabel(fout, dopt, cquery->data,
-				 NULL, binfo->rolname,
-				 binfo->dobj.catId, 0, binfo->dobj.dumpId);
+	if (binfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, cquery->data,
+					 NULL, binfo->rolname,
+					 binfo->dobj.catId, 0, binfo->dobj.dumpId);
 
-	/* Dump ACL if any */
-	if (binfo->blobacl)
+	/*
+	 * Dump ACL if any.
+	 *
+	 * Note that we have to check if blobacl is NULL, dumpACL doesn't.
+	 */
+	if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL))
 		dumpACL(fout, dopt, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
 				binfo->dobj.name, NULL, cquery->data,
 				NULL, binfo->rolname, binfo->blobacl);
@@ -3661,7 +3670,7 @@ getTypes(Archive *fout, int *numTypes)
 			 * dump it if the I/O or canonicalize functions need to be dumped;
 			 * this is taken care of while sorting dependencies.
 			 */
-			stinfo->dobj.dump = false;
+			stinfo->dobj.dump = DUMP_COMPONENT_NONE;
 
 			/*
 			 * However, if dumping from pre-7.3, there will be no dependency
@@ -3688,7 +3697,7 @@ getTypes(Archive *fout, int *numTypes)
 					addObjectDependency(&funcInfo->dobj,
 										stinfo->dobj.dumpId);
 					/* mark shell type as to be dumped */
-					stinfo->dobj.dump = true;
+					stinfo->dobj.dump = DUMP_COMPONENT_ALL;
 				}
 
 				funcInfo = findFuncByOid(typoutput);
@@ -3701,7 +3710,7 @@ getTypes(Archive *fout, int *numTypes)
 					addObjectDependency(&funcInfo->dobj,
 										stinfo->dobj.dumpId);
 					/* mark shell type as to be dumped */
-					stinfo->dobj.dump = true;
+					stinfo->dobj.dump = DUMP_COMPONENT_ALL;
 				}
 			}
 		}
@@ -4217,19 +4226,8 @@ getAggregates(Archive *fout, DumpOptions *dopt, int *numAggs)
 						  "(%s proowner) AS rolname, "
 						  "proacl AS aggacl "
 						  "FROM pg_proc p "
-						  "WHERE proisagg AND ("
-						  "pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')",
+						  "WHERE proisagg",
 						  username_subquery);
-		if (dopt->binary_upgrade && fout->remoteVersion >= 90100)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
 	}
 	else if (fout->remoteVersion >= 70300)
 	{
@@ -4240,9 +4238,7 @@ getAggregates(Archive *fout, DumpOptions *dopt, int *numAggs)
 						  "(%s proowner) AS rolname, "
 						  "proacl AS aggacl "
 						  "FROM pg_proc "
-						  "WHERE proisagg "
-						  "AND pronamespace != "
-			   "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')",
+						  "WHERE proisagg ",
 						  username_subquery);
 	}
 	else if (fout->remoteVersion >= 70100)
@@ -4365,11 +4361,11 @@ getFuncs(Archive *fout, DumpOptions *dopt, int *numFuncs)
 
 	/*
 	 * Find all user-defined functions.  Normally we can exclude functions in
-	 * pg_catalog, which is worth doing since there are several thousand of
-	 * 'em.  However, there are some extensions that create functions in
-	 * pg_catalog.  In normal dumps we can still ignore those --- but in
-	 * binary-upgrade mode, we must dump the member objects of the extension,
-	 * so be sure to fetch any such functions.
+	 * pg_catalog, provided their ACLs are still the default, which is worth
+	 * doing since there are several thousand of 'em.  However, there are
+	 * some extensions that create functions in pg_catalog.  In normal dumps we
+	 * can still ignore those --- but in binary-upgrade mode, we must dump the
+	 * member objects of the extension, so be sure to fetch any such functions.
 	 *
 	 * Also, in 9.2 and up, exclude functions that are internally dependent on
 	 * something else, since presumably those will be created as a result of
@@ -4387,24 +4383,13 @@ getFuncs(Archive *fout, DumpOptions *dopt, int *numFuncs)
 						  "pronamespace, "
 						  "(%s proowner) AS rolname "
 						  "FROM pg_proc p "
-						  "WHERE NOT proisagg AND ("
-						  "pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')",
+						  "WHERE NOT proisagg",
 						  username_subquery);
 		if (fout->remoteVersion >= 90200)
 			appendPQExpBufferStr(query,
 							   "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
 								 "WHERE classid = 'pg_proc'::regclass AND "
 								 "objid = p.oid AND deptype = 'i')");
-		if (dopt->binary_upgrade && fout->remoteVersion >= 90100)
-			appendPQExpBufferStr(query,
-							   "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
 	}
 	else if (fout->remoteVersion >= 70100)
 	{
@@ -5127,10 +5112,10 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 		 * Decide whether we want to dump this table.
 		 */
 		if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
-			tblinfo[i].dobj.dump = false;
+			tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
 		else
 			selectDumpableTable(&tblinfo[i]);
-		tblinfo[i].interesting = tblinfo[i].dobj.dump;
+		tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
 
 		tblinfo[i].postponed_def = false;		/* might get set during sort */
 
@@ -5203,7 +5188,7 @@ getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
 		if (owning_tab && owning_tab->dobj.dump)
 		{
 			seqinfo->interesting = true;
-			seqinfo->dobj.dump = true;
+			seqinfo->dobj.dump = DUMP_COMPONENT_ALL;
 		}
 	}
 }
@@ -8376,26 +8361,31 @@ dumpNamespace(Archive *fout, DumpOptions *dopt, NamespaceInfo *nspinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &nspinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
-				 nspinfo->dobj.name,
-				 NULL, NULL,
-				 nspinfo->rolname,
-				 false, "SCHEMA", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
+					 nspinfo->dobj.name,
+					 NULL, NULL,
+					 nspinfo->rolname,
+					 false, "SCHEMA", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Schema Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, nspinfo->rolname,
-				nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 NULL, nspinfo->rolname,
-				 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, nspinfo->rolname,
+					nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 NULL, nspinfo->rolname,
+					 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
-			qnspname, NULL, nspinfo->dobj.name, NULL,
-			nspinfo->rolname, nspinfo->nspacl);
+	if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
+				qnspname, NULL, nspinfo->dobj.name, NULL,
+				nspinfo->rolname, nspinfo->nspacl);
 
 	free(qnspname);
 
@@ -8504,22 +8494,26 @@ dumpExtension(Archive *fout, DumpOptions *dopt, ExtensionInfo *extinfo)
 
 	appendPQExpBuffer(labelq, "EXTENSION %s", qextname);
 
-	ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
-				 extinfo->dobj.name,
-				 NULL, NULL,
-				 "",
-				 false, "EXTENSION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
+					 extinfo->dobj.name,
+					 NULL, NULL,
+					 "",
+					 false, "EXTENSION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Extension Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 NULL, "",
-				 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+	if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+
+	if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 NULL, "",
+					 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
 
 	free(qextname);
 
@@ -8550,6 +8544,8 @@ dumpType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 		dumpEnumType(fout, dopt, tyinfo);
 	else if (tyinfo->typtype == TYPTYPE_RANGE)
 		dumpRangeType(fout, dopt, tyinfo);
+	else if (tyinfo->typtype == TYPTYPE_PSEUDO) /* can't */
+		return;
 	else
 		write_msg(NULL, "WARNING: typtype of data type \"%s\" appears to be invalid\n",
 				  tyinfo->dobj.name);
@@ -8654,28 +8650,33 @@ dumpEnumType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -8786,28 +8787,33 @@ dumpRangeType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -9176,28 +9182,33 @@ dumpBaseType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -9338,28 +9349,33 @@ dumpDomain(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "DOMAIN", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "DOMAIN", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Domain Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	/* Dump any per-constraint comments */
 	for (i = 0; i < tyinfo->nDomChecks; i++)
@@ -9371,10 +9387,13 @@ dumpDomain(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 						  fmtId(domcheck->dobj.name));
 		appendPQExpBuffer(labelq, "ON DOMAIN %s",
 						  fmtId(qtypname));
-		dumpComment(fout, dopt, labelq->data,
-					tyinfo->dobj.namespace->dobj.name,
-					tyinfo->rolname,
-					domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+		if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+			dumpComment(fout, dopt, labelq->data,
+						tyinfo->dobj.namespace->dobj.name,
+						tyinfo->rolname,
+						domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+
 		destroyPQExpBuffer(labelq);
 	}
 
@@ -9563,29 +9582,34 @@ dumpCompositeType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
-				 tyinfo->dobj.name,
-				 tyinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tyinfo->rolname, false,
-				 "TYPE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+					 tyinfo->dobj.name,
+					 tyinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tyinfo->rolname, false,
+					 "TYPE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 
 	/* Dump Type Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
-				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+					 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
-			qtypname, NULL, tyinfo->dobj.name,
-			tyinfo->dobj.namespace->dobj.name,
-			tyinfo->rolname, tyinfo->typacl);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
+				qtypname, NULL, tyinfo->dobj.name,
+				tyinfo->dobj.namespace->dobj.name,
+				tyinfo->rolname, tyinfo->typacl);
 
 	PQclear(res);
 	destroyPQExpBuffer(q);
@@ -9595,7 +9619,8 @@ dumpCompositeType(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 	destroyPQExpBuffer(query);
 
 	/* Dump any per-column comments */
-	dumpCompositeTypeColComments(fout, tyinfo);
+	if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpCompositeTypeColComments(fout, tyinfo);
 }
 
 /*
@@ -9739,15 +9764,16 @@ dumpShellType(Archive *fout, DumpOptions *dopt, ShellTypeInfo *stinfo)
 	appendPQExpBuffer(q, "CREATE TYPE %s;\n",
 					  fmtId(stinfo->dobj.name));
 
-	ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
-				 stinfo->dobj.name,
-				 stinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 stinfo->baseType->rolname, false,
-				 "SHELL TYPE", SECTION_PRE_DATA,
-				 q->data, "", NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
+					 stinfo->dobj.name,
+					 stinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 stinfo->baseType->rolname, false,
+					 "SHELL TYPE", SECTION_PRE_DATA,
+					 q->data, "", NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	destroyPQExpBuffer(q);
 }
@@ -9913,23 +9939,27 @@ dumpProcLang(Archive *fout, DumpOptions *dopt, ProcLangInfo *plang)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(defqry, &plang->dobj, labelq->data);
 
-	ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
-				 plang->dobj.name,
-				 lanschema, NULL, plang->lanowner,
-				 false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
-				 defqry->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
+					 plang->dobj.name,
+					 lanschema, NULL, plang->lanowner,
+					 false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
+					 defqry->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Proc Lang Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				plang->dobj.catId, 0, plang->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 NULL, "",
-				 plang->dobj.catId, 0, plang->dobj.dumpId);
+	if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					plang->dobj.catId, 0, plang->dobj.dumpId);
 
-	if (plang->lanpltrusted)
+	if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 NULL, "",
+					 plang->dobj.catId, 0, plang->dobj.dumpId);
+
+	if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
 		dumpACL(fout, dopt, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
 				qlanname, NULL, plang->dobj.name,
 				lanschema,
@@ -10540,28 +10570,33 @@ dumpFunc(Archive *fout, DumpOptions *dopt, FuncInfo *finfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &finfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
-				 funcsig_tag,
-				 finfo->dobj.namespace->dobj.name,
-				 NULL,
-				 finfo->rolname, false,
-				 "FUNCTION", SECTION_PRE_DATA,
-				 q->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
+					 funcsig_tag,
+					 finfo->dobj.namespace->dobj.name,
+					 NULL,
+					 finfo->rolname, false,
+					 "FUNCTION", SECTION_PRE_DATA,
+					 q->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
-	/* Dump Function Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				finfo->dobj.namespace->dobj.name, finfo->rolname,
-				finfo->dobj.catId, 0, finfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 finfo->dobj.namespace->dobj.name, finfo->rolname,
-				 finfo->dobj.catId, 0, finfo->dobj.dumpId);
+		/* Dump Function Comments and Security Labels */
+	if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					finfo->dobj.namespace->dobj.name, finfo->rolname,
+					finfo->dobj.catId, 0, finfo->dobj.dumpId);
+
+	if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 finfo->dobj.namespace->dobj.name, finfo->rolname,
+					 finfo->dobj.catId, 0, finfo->dobj.dumpId);
 
-	dumpACL(fout, dopt, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
-			funcsig, NULL, funcsig_tag,
-			finfo->dobj.namespace->dobj.name,
-			finfo->rolname, finfo->proacl);
+	if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
+				funcsig, NULL, funcsig_tag,
+				finfo->dobj.namespace->dobj.name,
+				finfo->rolname, finfo->proacl);
 
 	PQclear(res);
 
@@ -10668,18 +10703,20 @@ dumpCast(Archive *fout, DumpOptions *dopt, CastInfo *cast)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(defqry, &cast->dobj, labelq->data);
 
-	ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
-				 labelq->data,
-				 "pg_catalog", NULL, "",
-				 false, "CAST", SECTION_PRE_DATA,
-				 defqry->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
+					 labelq->data,
+					 "pg_catalog", NULL, "",
+					 false, "CAST", SECTION_PRE_DATA,
+					 defqry->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Cast Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				cast->dobj.catId, 0, cast->dobj.dumpId);
+	if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					cast->dobj.catId, 0, cast->dobj.dumpId);
 
 	destroyPQExpBuffer(defqry);
 	destroyPQExpBuffer(delqry);
@@ -11039,20 +11076,22 @@ dumpOpr(Archive *fout, DumpOptions *dopt, OprInfo *oprinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &oprinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
-				 oprinfo->dobj.name,
-				 oprinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 oprinfo->rolname,
-				 false, "OPERATOR", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
+					 oprinfo->dobj.name,
+					 oprinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 oprinfo->rolname,
+					 false, "OPERATOR", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Operator Comments */
-	dumpComment(fout, dopt, labelq->data,
-				oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
-				oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
+	if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
+					oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -11547,20 +11586,22 @@ dumpOpclass(Archive *fout, DumpOptions *dopt, OpclassInfo *opcinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &opcinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
-				 opcinfo->dobj.name,
-				 opcinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 opcinfo->rolname,
-				 false, "OPERATOR CLASS", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
+					 opcinfo->dobj.name,
+					 opcinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 opcinfo->rolname,
+					 false, "OPERATOR CLASS", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Operator Class Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, opcinfo->rolname,
-				opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
+	if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, opcinfo->rolname,
+					opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
 
 	free(amname);
 	destroyPQExpBuffer(query);
@@ -11860,20 +11901,22 @@ dumpOpfamily(Archive *fout, DumpOptions *dopt, OpfamilyInfo *opfinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &opfinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
-				 opfinfo->dobj.name,
-				 opfinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 opfinfo->rolname,
-				 false, "OPERATOR FAMILY", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
+					 opfinfo->dobj.name,
+					 opfinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 opfinfo->rolname,
+					 false, "OPERATOR FAMILY", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Operator Family Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, opfinfo->rolname,
-				opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
+	if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, opfinfo->rolname,
+					opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
 
 	free(amname);
 	PQclear(res_ops);
@@ -11949,20 +11992,22 @@ dumpCollation(Archive *fout, DumpOptions *dopt, CollInfo *collinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &collinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
-				 collinfo->dobj.name,
-				 collinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 collinfo->rolname,
-				 false, "COLLATION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
+					 collinfo->dobj.name,
+					 collinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 collinfo->rolname,
+					 false, "COLLATION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Collation Comments */
-	dumpComment(fout, dopt, labelq->data,
-				collinfo->dobj.namespace->dobj.name, collinfo->rolname,
-				collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
+	if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					collinfo->dobj.namespace->dobj.name, collinfo->rolname,
+					collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -12048,20 +12093,22 @@ dumpConversion(Archive *fout, DumpOptions *dopt, ConvInfo *convinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &convinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
-				 convinfo->dobj.name,
-				 convinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 convinfo->rolname,
-				 false, "CONVERSION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
+					 convinfo->dobj.name,
+					 convinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 convinfo->rolname,
+					 false, "CONVERSION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Conversion Comments */
-	dumpComment(fout, dopt, labelq->data,
-				convinfo->dobj.namespace->dobj.name, convinfo->rolname,
-				convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
+	if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					convinfo->dobj.namespace->dobj.name, convinfo->rolname,
+					convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -12444,23 +12491,27 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &agginfo->aggfn.dobj, labelq->data);
 
-	ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
-				 aggsig_tag,
-				 agginfo->aggfn.dobj.namespace->dobj.name,
-				 NULL,
-				 agginfo->aggfn.rolname,
-				 false, "AGGREGATE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
+					 aggsig_tag,
+					 agginfo->aggfn.dobj.namespace->dobj.name,
+					 NULL,
+					 agginfo->aggfn.rolname,
+					 false, "AGGREGATE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Aggregate Comments */
-	dumpComment(fout, dopt, labelq->data,
-			agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
-				agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-			agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
-				 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+				agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+					agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+				agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+					 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
 
 	/*
 	 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
@@ -12473,11 +12524,12 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo)
 	aggsig = format_function_signature(fout, &agginfo->aggfn, true);
 	aggsig_tag = format_function_signature(fout, &agginfo->aggfn, false);
 
-	dumpACL(fout, dopt, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
-			"FUNCTION",
-			aggsig, NULL, aggsig_tag,
-			agginfo->aggfn.dobj.namespace->dobj.name,
-			agginfo->aggfn.rolname, agginfo->aggfn.proacl);
+	if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
+				"FUNCTION",
+				aggsig, NULL, aggsig_tag,
+				agginfo->aggfn.dobj.namespace->dobj.name,
+				agginfo->aggfn.rolname, agginfo->aggfn.proacl);
 
 	free(aggsig);
 	if (aggfullsig)
@@ -12544,20 +12596,22 @@ dumpTSParser(Archive *fout, DumpOptions *dopt, TSParserInfo *prsinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &prsinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
-				 prsinfo->dobj.name,
-				 prsinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 "",
-				 false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
+					 prsinfo->dobj.name,
+					 prsinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 "",
+					 false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Parser Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
+	if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12631,20 +12685,22 @@ dumpTSDictionary(Archive *fout, DumpOptions *dopt, TSDictInfo *dictinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &dictinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
-				 dictinfo->dobj.name,
-				 dictinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 dictinfo->rolname,
-				 false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
+					 dictinfo->dobj.name,
+					 dictinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 dictinfo->rolname,
+					 false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Dictionary Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, dictinfo->rolname,
-				dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
+	if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, dictinfo->rolname,
+					dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12697,20 +12753,22 @@ dumpTSTemplate(Archive *fout, DumpOptions *dopt, TSTemplateInfo *tmplinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tmplinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
-				 tmplinfo->dobj.name,
-				 tmplinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 "",
-				 false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
+					 tmplinfo->dobj.name,
+					 tmplinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 "",
+					 false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Template Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, "",
-				tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
+	if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, "",
+					tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12825,20 +12883,22 @@ dumpTSConfig(Archive *fout, DumpOptions *dopt, TSConfigInfo *cfginfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &cfginfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
-				 cfginfo->dobj.name,
-				 cfginfo->dobj.namespace->dobj.name,
-				 NULL,
-				 cfginfo->rolname,
-				 false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
+					 cfginfo->dobj.name,
+					 cfginfo->dobj.namespace->dobj.name,
+					 NULL,
+					 cfginfo->rolname,
+					 false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump Configuration Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, cfginfo->rolname,
-				cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
+	if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, cfginfo->rolname,
+					cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -12899,27 +12959,30 @@ dumpForeignDataWrapper(Archive *fout, DumpOptions *dopt, FdwInfo *fdwinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &fdwinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
-				 fdwinfo->dobj.name,
-				 NULL,
-				 NULL,
-				 fdwinfo->rolname,
-				 false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
+					 fdwinfo->dobj.name,
+					 NULL,
+					 NULL,
+					 fdwinfo->rolname,
+					 false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Handle the ACL */
-	dumpACL(fout, dopt, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
-			"FOREIGN DATA WRAPPER",
-			qfdwname, NULL, fdwinfo->dobj.name,
-			NULL, fdwinfo->rolname,
-			fdwinfo->fdwacl);
+	if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
+				"FOREIGN DATA WRAPPER",
+				qfdwname, NULL, fdwinfo->dobj.name,
+				NULL, fdwinfo->rolname,
+				fdwinfo->fdwacl);
 
 	/* Dump Foreign Data Wrapper Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, fdwinfo->rolname,
-				fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
+	if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, fdwinfo->rolname,
+					fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
 
 	free(qfdwname);
 
@@ -12991,33 +13054,37 @@ dumpForeignServer(Archive *fout, DumpOptions *dopt, ForeignServerInfo *srvinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &srvinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
-				 srvinfo->dobj.name,
-				 NULL,
-				 NULL,
-				 srvinfo->rolname,
-				 false, "SERVER", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
+					 srvinfo->dobj.name,
+					 NULL,
+					 NULL,
+					 srvinfo->rolname,
+					 false, "SERVER", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Handle the ACL */
-	dumpACL(fout, dopt, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
-			"FOREIGN SERVER",
-			qsrvname, NULL, srvinfo->dobj.name,
-			NULL, srvinfo->rolname,
-			srvinfo->srvacl);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		dumpACL(fout, dopt, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
+				"FOREIGN SERVER",
+				qsrvname, NULL, srvinfo->dobj.name,
+				NULL, srvinfo->rolname,
+				srvinfo->srvacl);
 
 	/* Dump user mappings */
-	dumpUserMappings(fout,
-					 srvinfo->dobj.name, NULL,
-					 srvinfo->rolname,
-					 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
+		dumpUserMappings(fout,
+						 srvinfo->dobj.name, NULL,
+						 srvinfo->rolname,
+						 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
 
 	/* Dump Foreign Server Comments */
-	dumpComment(fout, dopt, labelq->data,
-				NULL, srvinfo->rolname,
-				srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
+	if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, srvinfo->rolname,
+					srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
 
 	free(qsrvname);
 
@@ -13179,15 +13246,16 @@ dumpDefaultACL(Archive *fout, DumpOptions *dopt, DefaultACLInfo *daclinfo)
 		exit_horribly(NULL, "could not parse default ACL list (%s)\n",
 					  daclinfo->defaclacl);
 
-	ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
-				 tag->data,
+	if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
+		ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
+					 tag->data,
 	   daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL,
-				 NULL,
-				 daclinfo->defaclrole,
-				 false, "DEFAULT ACL", SECTION_POST_DATA,
-				 q->data, "", NULL,
-				 NULL, 0,
-				 NULL, NULL);
+					 NULL,
+					 daclinfo->defaclrole,
+					 false, "DEFAULT ACL", SECTION_POST_DATA,
+					 q->data, "", NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	destroyPQExpBuffer(tag);
 	destroyPQExpBuffer(q);
@@ -13553,17 +13621,20 @@ collectSecLabels(Archive *fout, SecLabelItem **items)
 static void
 dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 {
-	if (tbinfo->dobj.dump && !dopt->dataOnly)
-	{
-		char	   *namecopy;
+	char	   *namecopy;
 
-		if (tbinfo->relkind == RELKIND_SEQUENCE)
-			dumpSequence(fout, dopt, tbinfo);
-		else
-			dumpTableSchema(fout, dopt, tbinfo);
+	if (!(tbinfo->dobj.dump & (DUMP_COMPONENT_DEFINITION|DUMP_COMPONENT_ACL)))
+		return;
 
-		/* Handle the ACL here */
-		namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
+	if (tbinfo->relkind == RELKIND_SEQUENCE)
+		dumpSequence(fout, dopt, tbinfo);
+	else
+		dumpTableSchema(fout, dopt, tbinfo);
+
+	namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
+
+	/* Handle the ACL here, if requested */
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
 		dumpACL(fout, dopt, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
 				(tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" :
 				"TABLE",
@@ -13571,47 +13642,51 @@ dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 				tbinfo->relacl);
 
-		/*
-		 * Handle column ACLs, if any.  Note: we pull these with a separate
-		 * query rather than trying to fetch them during getTableAttrs, so
-		 * that we won't miss ACLs on system columns.
-		 */
-		if (fout->remoteVersion >= 80400)
-		{
-			PQExpBuffer query = createPQExpBuffer();
-			PGresult   *res;
-			int			i;
+	/*
+	 * Handle column ACLs, if any.  Note: we pull these with a separate
+	 * query rather than trying to fetch them during getTableAttrs, so
+	 * that we won't miss ACLs on system columns.
+	 */
+	if (fout->remoteVersion >= 80400)
+	{
+		PQExpBuffer query = createPQExpBuffer();
+		PGresult   *res;
+		int			i;
 
-			appendPQExpBuffer(query,
-					   "SELECT attname, attacl FROM pg_catalog.pg_attribute "
-							  "WHERE attrelid = '%u' AND NOT attisdropped AND attacl IS NOT NULL "
-							  "ORDER BY attnum",
-							  tbinfo->dobj.catId.oid);
-			res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+		appendPQExpBuffer(query,
+				   "SELECT attname, attacl FROM pg_catalog.pg_attribute "
+						  "WHERE attrelid = '%u' AND NOT attisdropped AND attacl IS NOT NULL "
+						  "ORDER BY attnum",
+						  tbinfo->dobj.catId.oid);
+		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-			for (i = 0; i < PQntuples(res); i++)
-			{
-				char	   *attname = PQgetvalue(res, i, 0);
-				char	   *attacl = PQgetvalue(res, i, 1);
-				char	   *attnamecopy;
-				char	   *acltag;
+		for (i = 0; i < PQntuples(res); i++)
+		{
+			char	   *attname = PQgetvalue(res, i, 0);
+			char	   *attacl = PQgetvalue(res, i, 1);
+			char	   *attnamecopy;
+			char	   *acltag;
+
+			attnamecopy = pg_strdup(fmtId(attname));
+			acltag = psprintf("%s.%s", tbinfo->dobj.name, attname);
 
-				attnamecopy = pg_strdup(fmtId(attname));
-				acltag = psprintf("%s.%s", tbinfo->dobj.name, attname);
+			if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
 				/* Column's GRANT type is always TABLE */
 				dumpACL(fout, dopt, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
 						namecopy, attnamecopy, acltag,
 						tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 						attacl);
-				free(attnamecopy);
-				free(acltag);
-			}
-			PQclear(res);
-			destroyPQExpBuffer(query);
-		}
 
-		free(namecopy);
+			free(attnamecopy);
+			free(acltag);
+		}
+		PQclear(res);
+		destroyPQExpBuffer(query);
 	}
+
+	free(namecopy);
+
+	return;
 }
 
 /*
@@ -14279,24 +14354,27 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data);
 
-	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
-				 tbinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+					 tbinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
 			(tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
-				 tbinfo->rolname,
+					 tbinfo->rolname,
 			   (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
-				 reltypename,
-				 tbinfo->postponed_def ? SECTION_POST_DATA : SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+					 reltypename,
+					 tbinfo->postponed_def ? SECTION_POST_DATA : SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 
 	/* Dump Table Comments */
-	dumpTableComment(fout, dopt, tbinfo, reltypename);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpTableComment(fout, dopt, tbinfo, reltypename);
 
 	/* Dump Table Security Labels */
-	dumpTableSecLabel(fout, dopt, tbinfo, reltypename);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpTableSecLabel(fout, dopt, tbinfo, reltypename);
 
 	/* Dump comments on inlined table constraints */
 	for (j = 0; j < tbinfo->ncheck; j++)
@@ -14306,7 +14384,8 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 		if (constr->separate || !constr->conislocal)
 			continue;
 
-		dumpTableConstraintComment(fout, dopt, constr);
+		if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
+			dumpTableConstraintComment(fout, dopt, constr);
 	}
 
 	destroyPQExpBuffer(q);
@@ -14352,15 +14431,16 @@ dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo)
 	appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n",
 					  fmtId(tbinfo->attnames[adnum - 1]));
 
-	ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
-				 tbinfo->attnames[adnum - 1],
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname,
-				 false, "DEFAULT", SECTION_PRE_DATA,
-				 q->data, delq->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
+					 tbinfo->attnames[adnum - 1],
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname,
+					 false, "DEFAULT", SECTION_PRE_DATA,
+					 q->data, delq->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -14416,6 +14496,9 @@ dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo)
 	if (dopt->dataOnly)
 		return;
 
+	if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
+		return;
+
 	q = createPQExpBuffer();
 	delq = createPQExpBuffer();
 	labelq = createPQExpBuffer();
@@ -14466,24 +14549,26 @@ dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo)
 		appendPQExpBuffer(delq, "%s;\n",
 						  fmtId(indxinfo->dobj.name));
 
-		ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
-					 indxinfo->dobj.name,
-					 tbinfo->dobj.namespace->dobj.name,
-					 indxinfo->tablespace,
-					 tbinfo->rolname, false,
-					 "INDEX", SECTION_POST_DATA,
-					 q->data, delq->data, NULL,
-					 NULL, 0,
-					 NULL, NULL);
+		if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+			ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
+						 indxinfo->dobj.name,
+						 tbinfo->dobj.namespace->dobj.name,
+						 indxinfo->tablespace,
+						 tbinfo->rolname, false,
+						 "INDEX", SECTION_POST_DATA,
+						 q->data, delq->data, NULL,
+						 NULL, 0,
+						 NULL, NULL);
 	}
 
 	/* Dump Index Comments */
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name,
-				tbinfo->rolname,
-				indxinfo->dobj.catId, 0,
-				is_constraint ? indxinfo->indexconstraint :
-				indxinfo->dobj.dumpId);
+	if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name,
+					tbinfo->rolname,
+					indxinfo->dobj.catId, 0,
+					is_constraint ? indxinfo->indexconstraint :
+					indxinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
@@ -14589,15 +14674,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 						  fmtId(coninfo->dobj.name));
 
-		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-					 coninfo->dobj.name,
-					 tbinfo->dobj.namespace->dobj.name,
-					 indxinfo->tablespace,
-					 tbinfo->rolname, false,
-					 "CONSTRAINT", SECTION_POST_DATA,
-					 q->data, delq->data, NULL,
-					 NULL, 0,
-					 NULL, NULL);
+		if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+						 coninfo->dobj.name,
+						 tbinfo->dobj.namespace->dobj.name,
+						 indxinfo->tablespace,
+						 tbinfo->rolname, false,
+						 "CONSTRAINT", SECTION_POST_DATA,
+						 q->data, delq->data, NULL,
+						 NULL, 0,
+						 NULL, NULL);
 	}
 	else if (coninfo->contype == 'f')
 	{
@@ -14622,15 +14708,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 						  fmtId(coninfo->dobj.name));
 
-		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-					 coninfo->dobj.name,
-					 tbinfo->dobj.namespace->dobj.name,
-					 NULL,
-					 tbinfo->rolname, false,
-					 "FK CONSTRAINT", SECTION_POST_DATA,
-					 q->data, delq->data, NULL,
-					 NULL, 0,
-					 NULL, NULL);
+		if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+						 coninfo->dobj.name,
+						 tbinfo->dobj.namespace->dobj.name,
+						 NULL,
+						 tbinfo->rolname, false,
+						 "FK CONSTRAINT", SECTION_POST_DATA,
+						 q->data, delq->data, NULL,
+						 NULL, 0,
+						 NULL, NULL);
 	}
 	else if (coninfo->contype == 'c' && tbinfo)
 	{
@@ -14657,15 +14744,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 							  fmtId(coninfo->dobj.name));
 
-			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-						 coninfo->dobj.name,
-						 tbinfo->dobj.namespace->dobj.name,
-						 NULL,
-						 tbinfo->rolname, false,
-						 "CHECK CONSTRAINT", SECTION_POST_DATA,
-						 q->data, delq->data, NULL,
-						 NULL, 0,
-						 NULL, NULL);
+			if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+				ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+							 coninfo->dobj.name,
+							 tbinfo->dobj.namespace->dobj.name,
+							 NULL,
+							 tbinfo->rolname, false,
+							 "CHECK CONSTRAINT", SECTION_POST_DATA,
+							 q->data, delq->data, NULL,
+							 NULL, 0,
+							 NULL, NULL);
 		}
 	}
 	else if (coninfo->contype == 'c' && tbinfo == NULL)
@@ -14693,15 +14781,16 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
 							  fmtId(coninfo->dobj.name));
 
-			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
-						 coninfo->dobj.name,
-						 tyinfo->dobj.namespace->dobj.name,
-						 NULL,
-						 tyinfo->rolname, false,
-						 "CHECK CONSTRAINT", SECTION_POST_DATA,
-						 q->data, delq->data, NULL,
-						 NULL, 0,
-						 NULL, NULL);
+			if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+				ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+							 coninfo->dobj.name,
+							 tyinfo->dobj.namespace->dobj.name,
+							 NULL,
+							 tyinfo->rolname, false,
+							 "CHECK CONSTRAINT", SECTION_POST_DATA,
+							 q->data, delq->data, NULL,
+							 NULL, 0,
+							 NULL, NULL);
 		}
 	}
 	else
@@ -14711,7 +14800,7 @@ dumpConstraint(Archive *fout, DumpOptions *dopt, ConstraintInfo *coninfo)
 	}
 
 	/* Dump Constraint Comments --- only works for table constraints */
-	if (tbinfo && coninfo->separate)
+	if (tbinfo && coninfo->separate && coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
 		dumpTableConstraintComment(fout, dopt, coninfo);
 
 	destroyPQExpBuffer(q);
@@ -14735,11 +14824,17 @@ dumpTableConstraintComment(Archive *fout, DumpOptions *dopt, ConstraintInfo *con
 					  fmtId(coninfo->dobj.name));
 	appendPQExpBuffer(labelq, "ON %s",
 					  fmtId(tbinfo->dobj.name));
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name,
-				tbinfo->rolname,
-				coninfo->dobj.catId, 0,
-			 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
+
+	/*
+	 * The callers check this before calling us, so this shouldn't be necessary,
+	 * but it doesn't hurt to double-check here.
+	 */
+	if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name,
+					tbinfo->rolname,
+					coninfo->dobj.catId, 0,
+				 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(labelq);
 }
@@ -14932,15 +15027,16 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 		binary_upgrade_extension_member(query, &tbinfo->dobj,
 										labelq->data);
 
-	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
-				 tbinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname,
-				 false, "SEQUENCE", SECTION_PRE_DATA,
-				 query->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+					 tbinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname,
+					 false, "SEQUENCE", SECTION_PRE_DATA,
+					 query->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/*
 	 * If the sequence is owned by a table column, emit the ALTER for it as a
@@ -14968,25 +15064,29 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			appendPQExpBuffer(query, ".%s;\n",
 						fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
 
-			ArchiveEntry(fout, nilCatalogId, createDumpId(),
-						 tbinfo->dobj.name,
-						 tbinfo->dobj.namespace->dobj.name,
-						 NULL,
-						 tbinfo->rolname,
-						 false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
-						 query->data, "", NULL,
-						 &(tbinfo->dobj.dumpId), 1,
-						 NULL, NULL);
+			if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+				ArchiveEntry(fout, nilCatalogId, createDumpId(),
+							 tbinfo->dobj.name,
+							 tbinfo->dobj.namespace->dobj.name,
+							 NULL,
+							 tbinfo->rolname,
+							 false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
+							 query->data, "", NULL,
+							 &(tbinfo->dobj.dumpId), 1,
+							 NULL, NULL);
 		}
 	}
 
 	/* Dump Sequence Comments and Security Labels */
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-				tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
-	dumpSecLabel(fout, dopt, labelq->data,
-				 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-				 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+					tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
+		dumpSecLabel(fout, dopt, labelq->data,
+					 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+					 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
 	PQclear(res);
 
@@ -15035,15 +15135,16 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
 	appendPQExpBuffer(query, ", %s, %s);\n",
 					  last, (called ? "true" : "false"));
 
-	ArchiveEntry(fout, nilCatalogId, createDumpId(),
-				 tbinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname,
-				 false, "SEQUENCE SET", SECTION_DATA,
-				 query->data, "", NULL,
-				 &(tbinfo->dobj.dumpId), 1,
-				 NULL, NULL);
+	if (tbinfo->dobj.dump & DUMP_COMPONENT_DATA)
+		ArchiveEntry(fout, nilCatalogId, createDumpId(),
+					 tbinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname,
+					 false, "SEQUENCE SET", SECTION_DATA,
+					 query->data, "", NULL,
+					 &(tbinfo->dobj.dumpId), 1,
+					 NULL, NULL);
 
 	PQclear(res);
 
@@ -15241,19 +15342,21 @@ dumpTrigger(Archive *fout, DumpOptions *dopt, TriggerInfo *tginfo)
 	appendPQExpBuffer(labelq, "ON %s",
 					  fmtId(tbinfo->dobj.name));
 
-	ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
-				 tginfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname, false,
-				 "TRIGGER", SECTION_POST_DATA,
-				 query->data, delqry->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
+					 tginfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname, false,
+					 "TRIGGER", SECTION_POST_DATA,
+					 query->data, delqry->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-				tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
+	if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+					tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
 
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
@@ -15318,14 +15421,16 @@ dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo *evtinfo)
 	appendPQExpBuffer(labelq, "EVENT TRIGGER %s",
 					  fmtId(evtinfo->dobj.name));
 
-	ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
-				 evtinfo->dobj.name, NULL, NULL, evtinfo->evtowner, false,
-				 "EVENT TRIGGER", SECTION_POST_DATA,
-				 query->data, "", NULL, NULL, 0, NULL, NULL);
+	if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
+					 evtinfo->dobj.name, NULL, NULL, evtinfo->evtowner, false,
+					 "EVENT TRIGGER", SECTION_POST_DATA,
+					 query->data, "", NULL, NULL, 0, NULL, NULL);
 
-	dumpComment(fout, dopt, labelq->data,
-				NULL, evtinfo->evtowner,
-				evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
+	if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					NULL, evtinfo->evtowner,
+					evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
 
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(labelq);
@@ -15440,21 +15545,23 @@ dumpRule(Archive *fout, DumpOptions *dopt, RuleInfo *rinfo)
 	appendPQExpBuffer(labelq, " ON %s",
 					  fmtId(tbinfo->dobj.name));
 
-	ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
-				 rinfo->dobj.name,
-				 tbinfo->dobj.namespace->dobj.name,
-				 NULL,
-				 tbinfo->rolname, false,
-				 "RULE", SECTION_POST_DATA,
-				 cmd->data, delcmd->data, NULL,
-				 NULL, 0,
-				 NULL, NULL);
+	if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+		ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
+					 rinfo->dobj.name,
+					 tbinfo->dobj.namespace->dobj.name,
+					 NULL,
+					 tbinfo->rolname, false,
+					 "RULE", SECTION_POST_DATA,
+					 cmd->data, delcmd->data, NULL,
+					 NULL, 0,
+					 NULL, NULL);
 
 	/* Dump rule comments */
-	dumpComment(fout, dopt, labelq->data,
-				tbinfo->dobj.namespace->dobj.name,
-				tbinfo->rolname,
-				rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
+	if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+		dumpComment(fout, dopt, labelq->data,
+					tbinfo->dobj.namespace->dobj.name,
+					tbinfo->rolname,
+					rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
 
 	PQclear(res);
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 4c796ad..f4b2abd 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -80,6 +80,18 @@ typedef enum
 	DO_POLICY
 } DumpableObjectType;
 
+/* component types of an object which can be selected for dumping */
+typedef uint32 DumpComponents;	/* a bitmask of dump object components */
+#define DUMP_COMPONENT_NONE			(0)
+#define DUMP_COMPONENT_DEFINITION	(1 << 0)
+#define DUMP_COMPONENT_DATA			(1 << 1)
+#define DUMP_COMPONENT_COMMENT		(1 << 2)
+#define DUMP_COMPONENT_SECLABEL		(1 << 3)
+#define DUMP_COMPONENT_ACL			(1 << 4)
+#define DUMP_COMPONENT_POLICY		(1 << 5)
+#define DUMP_COMPONENT_USERMAP		(1 << 6)
+#define DUMP_COMPONENT_ALL			(0xFFFF)
+
 typedef struct _dumpableObject
 {
 	DumpableObjectType objType;
@@ -87,7 +99,7 @@ typedef struct _dumpableObject
 	DumpId		dumpId;			/* assigned by AssignDumpId() */
 	char	   *name;			/* object name (should never be NULL) */
 	struct _namespaceInfo *namespace;	/* containing namespace, or NULL */
-	bool		dump;			/* true if we want to dump this object */
+	DumpComponents dump;	/* bitmask of components to dump */
 	bool		ext_member;		/* true if object is member of extension */
 	DumpId	   *dependencies;	/* dumpIds of objects this one depends on */
 	int			nDeps;			/* number of valid dependencies */
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 7169ad0..8f89be7 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -792,9 +792,13 @@ dumpRoles(PGconn *conn)
 		 * for the role we are connected as, since even with --clean we will
 		 * have failed to drop it.  binary_upgrade cannot generate any errors,
 		 * so we assume the current role is already created.
+		 *
+		 * Any roles starting with pg_ get the same treatment as those are
+		 * created by initdb too.
 		 */
 		if (!binary_upgrade ||
-			strcmp(PQgetvalue(res, i, i_is_current_user), "f") == 0)
+			(strcmp(PQgetvalue(res, i, i_is_current_user), "f") == 0 &&
+			 strncmp(PQgetvalue(res, i, i_rolname), "pg_", 3)))
 			appendPQExpBuffer(buf, "CREATE ROLE %s;\n", fmtId(rolename));
 		appendPQExpBuffer(buf, "ALTER ROLE %s WITH", fmtId(rolename));
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 6db223a..f117840 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -23,6 +23,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -96,6 +97,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.4 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 904)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -603,7 +609,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -619,7 +626,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -952,6 +960,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.5 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e97e6b1..4c19e01 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2760,8 +2760,12 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
+DATA(insert OID = 3286 (  pg_stat_get_activity_all			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ _null_ pg_stat_get_activity _null_ _null_ _null_ ));
+DESCR("statistics: information about currently active backends, unfiltered");
 DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
+DATA(insert OID = 3285 (  pg_stat_get_wal_senders_all	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
+DESCR("statistics: information about currently active replication, unfiltered");
 DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
 DESCR("statistics: current backend PID");
 DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
@@ -3111,6 +3115,8 @@ DATA(insert OID = 2171 ( pg_cancel_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v
 DESCR("cancel a server process' current query");
 DATA(insert OID = 2096 ( pg_terminate_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
 DESCR("terminate a server process");
+DATA(insert OID = 3284 ( pg_signal_backend		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "23 23" _null_ _null_ _null_ _null_ _null_ pg_signal_backend _null_ _null_ _null_ ));
+DESCR("signal a server process");
 DATA(insert OID = 2172 ( pg_start_backup		PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 3220 "25 16" _null_ _null_ _null_ _null_ _null_ pg_start_backup _null_ _null_ _null_ ));
 DESCR("prepare for taking an online backup");
 DATA(insert OID = 2173 ( pg_stop_backup			PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_stop_backup _null_ _null_ _null_ ));
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
index b10e784..e458621 100644
--- a/src/include/replication/walsender.h
+++ b/src/include/replication/walsender.h
@@ -37,6 +37,7 @@ extern void WalSndWakeup(void);
 extern void WalSndRqstFileReload(void);
 
 extern Datum pg_stat_get_wal_senders(PG_FUNCTION_ARGS);
+extern Datum pg_stat_get_wal_senders_all(PG_FUNCTION_ARGS);
 
 /*
  * Remember that we want to wakeup walsenders later
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 33a453f..a126a76 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -482,6 +482,7 @@ extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
 extern Datum current_database(PG_FUNCTION_ARGS);
 extern Datum current_query(PG_FUNCTION_ARGS);
 extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+extern Datum pg_signal_backend(PG_FUNCTION_ARGS);
 extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
 extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
 extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 25095e5..4578300 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1637,6 +1637,28 @@ pg_stat_activity| SELECT s.datid,
     pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
     pg_authid u
   WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
+pg_stat_activity_all| SELECT s.datid,
+    d.datname,
+    s.pid,
+    s.usesysid,
+    u.rolname AS usename,
+    s.application_name,
+    s.client_addr,
+    s.client_hostname,
+    s.client_port,
+    s.backend_start,
+    s.xact_start,
+    s.query_start,
+    s.state_change,
+    s.waiting,
+    s.state,
+    s.backend_xid,
+    s.backend_xmin,
+    s.query
+   FROM pg_database d,
+    pg_stat_get_activity_all(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
+    pg_authid u
+  WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
 pg_stat_all_indexes| SELECT c.oid AS relid,
     i.oid AS indexrelid,
     n.nspname AS schemaname,
@@ -1744,6 +1766,26 @@ pg_stat_replication| SELECT s.pid,
     pg_authid u,
     pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
   WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+pg_stat_replication_all| SELECT s.pid,
+    s.usesysid,
+    u.rolname AS usename,
+    s.application_name,
+    s.client_addr,
+    s.client_hostname,
+    s.client_port,
+    s.backend_start,
+    s.backend_xmin,
+    w.state,
+    w.sent_location,
+    w.write_location,
+    w.flush_location,
+    w.replay_location,
+    w.sync_priority,
+    w.sync_state
+   FROM pg_stat_get_activity_all(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
+    pg_authid u,
+    pg_stat_get_wal_senders_all() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
+  WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
 pg_stat_ssl| SELECT s.pid,
     s.ssl,
     s.sslversion AS version,
-- 
1.9.1

#108Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#107)
Re: Additional role attributes && superuser review

On Wed, Apr 29, 2015 at 10:47 AM, Stephen Frost <sfrost@snowman.net> wrote:

Here is the latest revision of this patch.

I think this patch is too big and does too many things. It should be
broken up into small patches which can be discussed and validated
independently. The fact that your commit message is incredibly long
is a sign that there's too much going on here, and that message
doesn't even cover all of it.

It seems to me that the core change here is really to pg_dump. You're
adding the ability for pg_dump to dump and restore privileges on
objects in pg_dump. That capability is a complete - and useful -
feature in its own right, with no other changes.

Then the next thing you've got here is a series of patches that change
the rights to execute various utility functions. Some of those, like
the changes to pg_stop_backup(), are pretty much a slam-dunk, because
they don't really change user-visible behavior; they just give the DBA
more options. Others, like the changes to replication permissions,
are likely to be more controversial. You should split the stuff
that's a slam-dunk apart from the stuff that makes policy decisions,
and plan to commit the former changes first, and the latter changes
only if and when there is very clear agreement on the specific
policies to be changed.

Finally, you've got the idea of making pg_ a reserved prefix for
roles, adding some predefined roles, and giving them some predefined
privileges. That should be yet another patch.

I think that if you commit this the way you have it today, everybody
will go, oh, look, Stephen committed something, but it looks
complicated, I won't pay attention. And then, six months from now
when we're in beta, or maybe after final, people will start looking at
this and realizing that there are parts of it they hate, but it will
be hard to fix at that point. Breaking it out will hopefully allow
more discussion on the individual features of in here, of which there
are probably at least four. It will also make it easier to revert
part of it rather than all of it if that turns out to be needed.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#109Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Robert Haas (#108)
Re: Additional role attributes && superuser review

Robert Haas wrote:

I think that if you commit this the way you have it today, everybody
will go, oh, look, Stephen committed something, but it looks
complicated, I won't pay attention.

Yeah, that sucks.

Finally, you've got the idea of making pg_ a reserved prefix for
roles, adding some predefined roles, and giving them some predefined
privileges. That should be yet another patch.

On this part I have a bit of a problem -- the prefix is not really
reserved, is it. I mean, evidently it's still possible to create roles
with the pg_ prefix ... otherwise, how come the new lines to
system_views.sql that create the "predefined" roles work in the first
place? I think if we're going to reserve role names, we should reserve
them for real: CREATE ROLE should flat out reject creation of such
roles, and the default ones should be created during bootstrap.

IMO anyway.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#110Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Alvaro Herrera (#109)
Re: Additional role attributes && superuser review

On 30/04/15 12:20, Alvaro Herrera wrote:

Robert Haas wrote:

I think that if you commit this the way you have it today, everybody
will go, oh, look, Stephen committed something, but it looks
complicated, I won't pay attention.

Yeah, that sucks.

Finally, you've got the idea of making pg_ a reserved prefix for
roles, adding some predefined roles, and giving them some predefined
privileges. That should be yet another patch.

On this part I have a bit of a problem -- the prefix is not really
reserved, is it. I mean, evidently it's still possible to create roles
with the pg_ prefix ... otherwise, how come the new lines to
system_views.sql that create the "predefined" roles work in the first
place? I think if we're going to reserve role names, we should reserve
them for real: CREATE ROLE should flat out reject creation of such
roles, and the default ones should be created during bootstrap.

IMO anyway.

What if I had a company with several subsidiaries using the same
database, and want to prefix roles and other things with the
subsidiary's initials? (I am not saying this would be a good
architecture!!!)

For example if one subsidiary was called 'Perfect Gentleman', so I would
want roles prefixed by 'pg_' and would be annoyed if I couldn't!

Cheers,
Gavin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#111Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#109)
Re: Additional role attributes && superuser review

On Wed, Apr 29, 2015 at 8:20 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Finally, you've got the idea of making pg_ a reserved prefix for
roles, adding some predefined roles, and giving them some predefined
privileges. That should be yet another patch.

On this part I have a bit of a problem -- the prefix is not really
reserved, is it. I mean, evidently it's still possible to create roles
with the pg_ prefix ... otherwise, how come the new lines to
system_views.sql that create the "predefined" roles work in the first
place? I think if we're going to reserve role names, we should reserve
them for real: CREATE ROLE should flat out reject creation of such
roles, and the default ones should be created during bootstrap.

IMO anyway.

This is exactly what I mean about needing separate discussion for
separate parts of the patch. There's so much different stuff in there
right now that objections like this won't necessarily come out until
it's far too late to change things around.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#112Stephen Frost
sfrost@snowman.net
In reply to: Gavin Flower (#110)
Re: Additional role attributes && superuser review

Gavin,

* Gavin Flower (GavinFlower@archidevsys.co.nz) wrote:

What if I had a company with several subsidiaries using the same
database, and want to prefix roles and other things with the
subsidiary's initials? (I am not saying this would be a good
architecture!!!)

If you admit that it's not a good solution then I'm not quite sure how
much we really want to worry about it. :)

For example if one subsidiary was called 'Perfect Gentleman', so I
would want roles prefixed by 'pg_' and would be annoyed if I
couldn't!

You might try creating a schema for that user.. You'll hopefully find
it difficult to do. :)

In consideration of the fact that you can't create schemas which start
with "pg_" and therefore the default search_path wouldn't work for that
user, and that we also reserve "pg_" for tablespaces, I'm not inclined
to worry too much about this case. Further, if we accept this argument,
then we simply can't ever provide additional default or system roles,
ever. That'd be a pretty narrow corner to have painted ourselves into.

Thanks!

Stephen

#113Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Stephen Frost (#112)
Re: Additional role attributes && superuser review

On 05/08/2015 07:35 AM, Stephen Frost wrote:

Gavin,

* Gavin Flower (GavinFlower@archidevsys.co.nz) wrote:

What if I had a company with several subsidiaries using the same
database, and want to prefix roles and other things with the
subsidiary's initials? (I am not saying this would be a good
architecture!!!)

If you admit that it's not a good solution then I'm not quite sure how
much we really want to worry about it. :)

For example if one subsidiary was called 'Perfect Gentleman', so I
would want roles prefixed by 'pg_' and would be annoyed if I
couldn't!

You might try creating a schema for that user.. You'll hopefully find
it difficult to do. :)

In consideration of the fact that you can't create schemas which start
with "pg_" and therefore the default search_path wouldn't work for that
user, and that we also reserve "pg_" for tablespaces, I'm not inclined
to worry too much about this case. Further, if we accept this argument,
then we simply can't ever provide additional default or system roles,
ever. That'd be a pretty narrow corner to have painted ourselves into.

Well, you could still provide them through some other mechanism, like
require typing "SYSTEM ROLE pg_backup" any time you mean that magic
role. But I agree, reserving pg_* is much better. I wish we had done it
when we invented roles (6.5?), so there would be no risk that you would
upgrade from a system that already has a "pg_foo" role. But I think it'd
still be OK.

I agree with Robert's earlier point that this needs to be split into
multiple patches, which can then be reviewed and discussed separately.
Pending that, I'm going to mark this as "Waiting on author" in the
commitfest.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#114Michael Paquier
michael.paquier@gmail.com
In reply to: Heikki Linnakangas (#113)
Re: Additional role attributes && superuser review

On Sat, Jul 11, 2015 at 6:06 AM, Heikki Linnakangas wrote:

On 05/08/2015 07:35 AM, Stephen Frost wrote:

In consideration of the fact that you can't create schemas which start
with "pg_" and therefore the default search_path wouldn't work for that
user, and that we also reserve "pg_" for tablespaces, I'm not inclined
to worry too much about this case. Further, if we accept this argument,
then we simply can't ever provide additional default or system roles,
ever. That'd be a pretty narrow corner to have painted ourselves into.

Well, you could still provide them through some other mechanism, like
require typing "SYSTEM ROLE pg_backup" any time you mean that magic role.
But I agree, reserving pg_* is much better. I wish we had done it when we
invented roles (6.5?), so there would be no risk that you would upgrade from
a system that already has a "pg_foo" role. But I think it'd still be OK.

I agree with Robert's earlier point that this needs to be split into
multiple patches, which can then be reviewed and discussed separately.
Pending that, I'm going to mark this as "Waiting on author" in the
commitfest.

... And now marked as returned with feedback.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#115Stephen Frost
sfrost@snowman.net
In reply to: Heikki Linnakangas (#113)
1 attachment(s)
Re: Additional role attributes && superuser review

* Heikki Linnakangas (hlinnaka@iki.fi) wrote:

I agree with Robert's earlier point that this needs to be split into
multiple patches, which can then be reviewed and discussed
separately. Pending that, I'm going to mark this as "Waiting on
author" in the commitfest.

Attached is an initial split which divides up reserving the role names
from actually adding the initial set of default roles.

Thanks!

Stephen

Attachments:

default_roles_v6.patchtext/x-diff; charset=us-asciiDownload
From 593ae64bf22bc343d7a5824d65c1224a77091710 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:04:55 -0400
Subject: [PATCH 1/2] Reserve the "pg_" namespace for roles

This will prevent users from creating roles which being with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.
---
 src/backend/catalog/catalog.c |  5 +++--
 src/backend/commands/user.c   | 32 ++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dumpall.c  |  2 ++
 src/bin/pg_upgrade/check.c    | 40 ++++++++++++++++++++++++++++++++++++++--
 src/bin/psql/command.c        |  4 ++--
 src/bin/psql/describe.c       |  5 ++++-
 src/bin/psql/describe.h       |  2 +-
 src/bin/psql/help.c           |  4 ++--
 8 files changed, 84 insertions(+), 10 deletions(-)

diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 295e0b0..fde8d15 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role is the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 41d4606..d115b2a 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8156b76..008726c 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -431,7 +431,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -476,7 +476,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 898f8b3..703e065 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2639,7 +2639,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2684,6 +2684,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5b63e76..ff60a85 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
-- 
1.9.1


From 2a142cd8b1cf2ca31b4f975ee7912a4b8ae67a7d Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:08:03 -0400
Subject: [PATCH 2/2] Create default roles

---
 contrib/test_decoding/expected/permissions.out |  8 +--
 doc/src/sgml/catalogs.sgml                     |  9 +++
 doc/src/sgml/user-manag.sgml                   | 91 ++++++++++++++++++++++++++
 src/backend/access/transam/xlogfuncs.c         | 30 +++++----
 src/backend/replication/logical/logicalfuncs.c | 17 ++---
 src/backend/replication/slotfuncs.c            | 29 ++++----
 src/backend/replication/walsender.c            |  8 ++-
 src/backend/utils/adt/misc.c                   | 12 ++--
 src/backend/utils/adt/pgstatfuncs.c            | 25 ++++---
 src/backend/utils/misc/guc.c                   |  7 ++
 src/include/catalog/pg_authid.h                | 19 +++++-
 11 files changed, 200 insertions(+), 55 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index e8bd3a1..db05afc 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,15 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <para>
+   Also note that changing the permissions on objects in the system
+   catalogs, while possible, is unlikely to have the desired effect as
+   the internal lookup functions use a cache and do not check the
+   permissions nor policies of tables in the system catalog.  Further,
+   permission changes to objects in the system catalogs are not
+   preserved by pg_dump or across upgrades.
+  </para>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 6476e3f..2b22557 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -414,6 +414,97 @@ DROP ROLE <replaceable>name</replaceable>;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch xlogs, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_montior</entry>
+       <entry>To privileged system information (eg: activity of other users, replication lag)</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume xlog replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Request logfile rotation</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate)</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all config files</entry>
+      </row>
+      <row>
+       <entry>pg_admin</entry>
+       <entry>
+        Granted pg_backup, pg_monitor, pg_reply, pg_replication,
+        pg_rotate_logfile, pg_signal_backend and pg_file_settings roles.
+       </entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+GRANT pg_admin TO admin_user;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..dda39ad 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +145,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -348,10 +354,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +376,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index a354a3f..766987f 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c95fe75..842aa95 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2803,11 +2805,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index c0495d9..6da052b 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being cancelled"))));
+				 (errmsg("must be a member of the role whose query is being cancelled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17053af..009925f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8198,6 +8200,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all config file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index d5f19d6..852aac4 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -96,7 +96,24 @@ typedef FormData_pg_authid *Form_pg_authid;
  * ----------------
  */
 DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 3288 ( "pg_admin" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3289 ( "pg_monitor" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3290 ( "pg_backup" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3291 ( "pg_replay" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3292 ( "pg_replication" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3293 ( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3294 ( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3295 ( "pg_file_settings" f t f f f f f -1 _null_ _null_));
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define BOOTSTRAP_SUPERUSERID			10
+
+#define DEFAULT_ROLE_ADMINID			3288
+#define DEFAULT_ROLE_MONITORID			3289
+#define DEFAULT_ROLE_BACKUPID			3290
+#define DEFAULT_ROLE_REPLAYID			3291
+#define DEFAULT_ROLE_REPLICATIONID		3292
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	3293
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	3294
+#define DEFAULT_ROLE_FILE_SETTINGSID	3295
 
 #endif   /* PG_AUTHID_H */
-- 
1.9.1

#116Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#115)
1 attachment(s)
Re: Additional role attributes && superuser review

On Wed, Sep 30, 2015 at 8:11 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Heikki Linnakangas (hlinnaka@iki.fi) wrote:

I agree with Robert's earlier point that this needs to be split into
multiple patches, which can then be reviewed and discussed
separately. Pending that, I'm going to mark this as "Waiting on
author" in the commitfest.

Attached is an initial split which divides up reserving the role names
from actually adding the initial set of default roles.

I have begun looking at this patch, and here are some comments after
screening it.

Patch needs a rebase, some catalog OIDs and there was a conflict in misc.c
(see attached for the rebase. none of the comments mentioning issues are
fixed by it).

=# grant pg_replay to pg_backup ;
GRANT ROLE
=# \du pg_backup
List of roles
Role name | Attributes | Member of
-----------+--------------+-------------
pg_backup | Cannot login | {pg_replay}
Perhaps we should restrict granting a default role to another default role?

+  <para>
+   Also note that changing the permissions on objects in the system
+   catalogs, while possible, is unlikely to have the desired effect as
+   the internal lookup functions use a cache and do not check the
+   permissions nor policies of tables in the system catalog.  Further,
+   permission changes to objects in the system catalogs are not
+   preserved by pg_dump or across upgrades.
+  </para>
This bit could be perhaps applied as a separate patch.
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch xlogs, and create restore
points.</entry>
+      </row>
s/switch xlogs/switch to a new transaction log file/
It seems weird to not have a dedicated role for pg_switch_xlog.

In func.sgml, the descriptions of pg_switch_xlog, pg_xlog_replay_pause and
pg_xlog_replay_resume still mention that those functions are restricted
only to superusers. The documentation needs an update. It would be good to
double-check if there are similar mistakes for the other functions.

+ <entry>pg_montior</entry>
Typo, pg_monitor.

+ <entry>Pause and resume xlog replay on replicas.</entry>
Those descriptions should be complete sentences or not? Both styles are
mixed in user-manag.sgml.

+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
pg_replication_slots would be more adapted?
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all config files</entry>
+      </row>
I would say "configuration files" instead. An abbreviation is not adapted.
+       <entry>pg_admin</entry>
+       <entry>
+        Granted pg_backup, pg_monitor, pg_reply, pg_replication,
+        pg_rotate_logfile, pg_signal_backend and pg_file_settings roles.
+       </entry>
Typo, s/pg_reply/pg_replay and I think that there should be <literal>
markups around those role names. I am actually not convinced that we
actually need it.
+       if (IsReservedName(stmt->role))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        stmt->role),
+                                errdetail("Role names starting with
\"pg_\" are reserved.")));
Perhaps this could be a separate change? It seems that restricting roles
with pg_ on the cluster is not a bad restriction in any case. You may want
to add regression tests to trigger those errors as well.

Shouldn't pg_current_xlog_location, pg_current_xlog_insert_location,
pg_last_xlog_receive_location and pg_last_xlog_replay_location fall under a
restriction category like pg_monitor? I recall of course that we discussed
that some months ago and that a lot of people were reluctant to harden this
area to not break any existing monitoring tool, still this patch may be the
occasion to restrict a bit those functions, particularly on servers where
wal_compression is enabled.

It would be nice to have regression tests as well to check that role
restrictions are applied where they should.
Regards,
--
Michael

Attachments:

20151118_default_roles.patchtext/x-diff; charset=US-ASCII; name=20151118_default_roles.patchDownload
diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 97ef618..89d3ae0 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,15 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <para>
+   Also note that changing the permissions on objects in the system
+   catalogs, while possible, is unlikely to have the desired effect as
+   the internal lookup functions use a cache and do not check the
+   permissions nor policies of tables in the system catalog.  Further,
+   permission changes to objects in the system catalogs are not
+   preserved by pg_dump or across upgrades.
+  </para>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 1952cac..66e2493 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -472,6 +472,97 @@ DROP ROLE doomed_role;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch xlogs, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_montior</entry>
+       <entry>To privileged system information (eg: activity of other users, replication lag)</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume xlog replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Request logfile rotation</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate)</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all config files</entry>
+      </row>
+      <row>
+       <entry>pg_admin</entry>
+       <entry>
+        Granted pg_backup, pg_monitor, pg_reply, pg_replication,
+        pg_rotate_logfile, pg_signal_backend and pg_file_settings roles.
+       </entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+GRANT pg_admin TO admin_user;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..dda39ad 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +145,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -348,10 +354,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +376,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 295e0b0..fde8d15 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role is the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 012987a..968ad93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4a4643e..bf0194c 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2808,11 +2810,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index 3ef6e43..6da052b 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being canceled"))));
+				 (errmsg("must be a member of the role whose query is being cancelled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..e28eae3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8249,6 +8251,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all config file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 41d4606..d115b2a 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 72c00c1..3e7cc3f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -431,7 +431,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -476,7 +476,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 92ed6e2..488693a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2647,7 +2647,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2692,6 +2692,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5b63e76..ff60a85 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index 2c8565e..16e3474 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -95,8 +95,25 @@ typedef FormData_pg_authid *Form_pg_authid;
  * user choices.
  * ----------------
  */
-DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 10	( "POSTGRES"		  t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 3317	( "pg_admin"		  f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3318	( "pg_monitor"		  f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3319	( "pg_backup"		  f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3320	( "pg_replay"		  f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3321	( "pg_replication"	  f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3322	( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3323	( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 3324	( "pg_file_settings"  f t f f f f f -1 _null_ _null_));
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define BOOTSTRAP_SUPERUSERID			10
+
+#define DEFAULT_ROLE_ADMINID			3317
+#define DEFAULT_ROLE_MONITORID			3318
+#define DEFAULT_ROLE_BACKUPID			3319
+#define DEFAULT_ROLE_REPLAYID			3320
+#define DEFAULT_ROLE_REPLICATIONID		3321
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	3322
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	3323
+#define DEFAULT_ROLE_FILE_SETTINGSID	3324
 
 #endif   /* PG_AUTHID_H */
#117Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#116)
Re: Additional role attributes && superuser review

On Wed, Nov 18, 2015 at 10:06 PM, Michael Paquier <michael.paquier@gmail.com

wrote:

On Wed, Sep 30, 2015 at 8:11 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Heikki Linnakangas (hlinnaka@iki.fi) wrote:

I agree with Robert's earlier point that this needs to be split into
multiple patches, which can then be reviewed and discussed
separately. Pending that, I'm going to mark this as "Waiting on
author" in the commitfest.

Attached is an initial split which divides up reserving the role names
from actually adding the initial set of default roles.

I have begun looking at this patch, and here are some comments after
screening it.

Patch needs a rebase, some catalog OIDs and there was a conflict in misc.c
(see attached for the rebase. none of the comments mentioning issues are
fixed by it).

=# grant pg_replay to pg_backup ;
GRANT ROLE
=# \du pg_backup
List of roles
Role name | Attributes | Member of
-----------+--------------+-------------
pg_backup | Cannot login | {pg_replay}
Perhaps we should restrict granting a default role to another default role?

+  <para>
+   Also note that changing the permissions on objects in the system
+   catalogs, while possible, is unlikely to have the desired effect as
+   the internal lookup functions use a cache and do not check the
+   permissions nor policies of tables in the system catalog.  Further,
+   permission changes to objects in the system catalogs are not
+   preserved by pg_dump or across upgrades.
+  </para>
This bit could be perhaps applied as a separate patch.
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch xlogs, and create restore
points.</entry>
+      </row>
s/switch xlogs/switch to a new transaction log file/
It seems weird to not have a dedicated role for pg_switch_xlog.

In func.sgml, the descriptions of pg_switch_xlog, pg_xlog_replay_pause and
pg_xlog_replay_resume still mention that those functions are restricted
only to superusers. The documentation needs an update. It would be good to
double-check if there are similar mistakes for the other functions.

+ <entry>pg_montior</entry>
Typo, pg_monitor.

+ <entry>Pause and resume xlog replay on replicas.</entry>
Those descriptions should be complete sentences or not? Both styles are
mixed in user-manag.sgml.

+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
pg_replication_slots would be more adapted?
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all config
files</entry>
+      </row>
I would say "configuration files" instead. An abbreviation is not adapted.
+       <entry>pg_admin</entry>
+       <entry>
+        Granted pg_backup, pg_monitor, pg_reply, pg_replication,
+        pg_rotate_logfile, pg_signal_backend and pg_file_settings roles.
+       </entry>
Typo, s/pg_reply/pg_replay and I think that there should be <literal>
markups around those role names. I am actually not convinced that we
actually need it.
+       if (IsReservedName(stmt->role))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        stmt->role),
+                                errdetail("Role names starting with
\"pg_\" are reserved.")));
Perhaps this could be a separate change? It seems that restricting roles
with pg_ on the cluster is not a bad restriction in any case. You may want
to add regression tests to trigger those errors as well.

Shouldn't pg_current_xlog_location, pg_current_xlog_insert_location,
pg_last_xlog_receive_location and pg_last_xlog_replay_location fall under a
restriction category like pg_monitor? I recall of course that we discussed
that some months ago and that a lot of people were reluctant to harden this
area to not break any existing monitoring tool, still this patch may be the
occasion to restrict a bit those functions, particularly on servers where
wal_compression is enabled.

It would be nice to have regression tests as well to check that role
restrictions are applied where they should.

Bonus:
-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"))));
-}
I would have let check_permissions and directly done the checks on
has_rolreplication and has_privs_of_role in it, the same code being
duplicated three times.
--
Michael

#118Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#117)
1 attachment(s)
Re: Additional role attributes && superuser review

Michael,

Thanks for the review!

* Michael Paquier (michael.paquier@gmail.com) wrote:

Patch needs a rebase, some catalog OIDs and there was a conflict in misc.c
(see attached for the rebase. none of the comments mentioning issues are
fixed by it).

Done (did it a bit differently from what you had, to hopefully avoid
future OID conflicts and also to allow us a bit of room to add
additional default roles later, if we choose, in nearby OID space).

=# grant pg_replay to pg_backup ;
GRANT ROLE
=# \du pg_backup
List of roles
Role name | Attributes | Member of
-----------+--------------+-------------
pg_backup | Cannot login | {pg_replay}
Perhaps we should restrict granting a default role to another default role?

Done (in the second patch in the series, the one reserving 'pg_').

+  <para>
+   Also note that changing the permissions on objects in the system
+   catalogs, while possible, is unlikely to have the desired effect as
+   the internal lookup functions use a cache and do not check the
+   permissions nor policies of tables in the system catalog.  Further,
+   permission changes to objects in the system catalogs are not
+   preserved by pg_dump or across upgrades.
+  </para>
This bit could be perhaps applied as a separate patch.

Done as a separate patch (first one in the series).

+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch xlogs, and create restore
points.</entry>
+      </row>
s/switch xlogs/switch to a new transaction log file/

Fixed.

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

In func.sgml, the descriptions of pg_switch_xlog, pg_xlog_replay_pause and
pg_xlog_replay_resume still mention that those functions are restricted
only to superusers. The documentation needs an update. It would be good to
double-check if there are similar mistakes for the other functions.

Fixed. Also did a review and found a number of other places which
required updating, so those have also been fixed.

+ <entry>pg_montior</entry>
Typo, pg_monitor.

Fixed.

+ <entry>Pause and resume xlog replay on replicas.</entry>
Those descriptions should be complete sentences or not? Both styles are
mixed in user-manag.sgml.

I didn't take any action on this.

+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
pg_replication_slots would be more adapted?

Perhaps... I didn't make this change, but if others agree that adding
"_slots" would help, I'd be happy to do so. Personally, I'd like to
keep these names shorter, if possible, but I don't want it to be
confusing either.

+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all config files</entry>
+      </row>
I would say "configuration files" instead. An abbreviation is not adapted.

Done.

+       <entry>pg_admin</entry>
+       <entry>
+        Granted pg_backup, pg_monitor, pg_reply, pg_replication,
+        pg_rotate_logfile, pg_signal_backend and pg_file_settings roles.
+       </entry>
Typo, s/pg_reply/pg_replay and I think that there should be <literal>
markups around those role names. I am actually not convinced that we
actually need it.

I added <literal> markups around the role names, where used.

I'm guessing you were referring to pg_admin with your comment that you
were "not convinced that we actually need it." After thinking about
it for a bit, I tend to agree. A user could certainly create their own
role which combines these all together if they wanted to (or do any
other combinations, based on their particular needs). I've gone ahead
and removed pg_admin from the set of default roles.

+       if (IsReservedName(stmt->role))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        stmt->role),
+                                errdetail("Role names starting with
\"pg_\" are reserved.")));
Perhaps this could be a separate change? It seems that restricting roles
with pg_ on the cluster is not a bad restriction in any case. You may want
to add regression tests to trigger those errors as well.

I'm a bit confused- this is a separate change. Note that the previous
patch was actually a git patchset which had two patches- one to do the
reservation and a second to add the default roles. The attached
patchset is actually three patches:

1- Update to catalog documentation regarding permissions
2- Reserve 'pg_' role namespace
3- Add default roles

Shouldn't pg_current_xlog_location, pg_current_xlog_insert_location,
pg_last_xlog_receive_location and pg_last_xlog_replay_location fall under a
restriction category like pg_monitor? I recall of course that we discussed
that some months ago and that a lot of people were reluctant to harden this
area to not break any existing monitoring tool, still this patch may be the
occasion to restrict a bit those functions, particularly on servers where
wal_compression is enabled.

For my 2c, I believe they should. I've not modified them in that way in
this patchset, but I can certainly do so if others agree.

It would be nice to have regression tests as well to check that role
restrictions are applied where they should.

I've added regression tests for the default roles where it was
relatively straight-forward to do so. I don't see a trivial way to test
that the pg_signal_backend role works though, as an example.

Bonus:
-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"))));
-}
I would have let check_permissions and directly done the checks on
has_rolreplication and has_privs_of_role in it, the same code being
duplicated three times.

For my 2c, I dislike the notion of "check_permissions()" functions being
added throughout the codebase as I'm afraid it'd get confusing, which is
why I got rid of it. I'm much happier seeing the actual permissions
check as it should be happening early on in the primary function which
is being called into. If there is really a push to go back to having a
'check_permissions()' like function, I'd argue that we should make the
function name more descriptive of what's actually being done and have
them be distinct from each other. As I recall, I discussed this change
with Andres and he didn't feel particularly strongly about this one way
or the other, therefore, I've not made this change for this patchset.

Thanks!

Stephen

Attachments:

default_roles_v7.patchtext/x-diff; charset=us-asciiDownload
From 20b942a6957c6cbe54c92b0eda09ab08a4b3bc04 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 18 Nov 2015 11:50:57 -0500
Subject: [PATCH 1/3] Add note regarding permissions in pg_catalog

Add a note to the system catalog section pointing out that while
modifying the permissions on catalog tables is possible, it's
unlikely to have the desired effect.
---
 doc/src/sgml/catalogs.sgml | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 97ef618..3b7768c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,17 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <note>
+   <para>
+    Changing the permissions on objects in the system catalogs, while
+    possible, is unlikely to have the desired effect as the internal
+    lookup functions use a cache and do not check the permissions nor
+    policies of tables in the system catalog.  Further, permission
+    changes to objects in the system catalogs are not preserved by
+    pg_dump or across upgrades.
+   </para>
+  </note>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
-- 
2.5.0


From 20ddd329e486bdb23ee96e0c1956c7d45596781a Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:04:55 -0400
Subject: [PATCH 2/3] Reserve the "pg_" namespace for roles

This will prevent users from creating roles which being with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.
---
 src/backend/catalog/catalog.c |  5 +++--
 src/backend/commands/user.c   | 38 ++++++++++++++++++++++++++++++++++++++
 src/backend/utils/adt/acl.c   | 26 ++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dumpall.c  |  2 ++
 src/bin/pg_upgrade/check.c    | 40 ++++++++++++++++++++++++++++++++++++++--
 src/bin/psql/command.c        |  4 ++--
 src/bin/psql/describe.c       |  5 ++++-
 src/bin/psql/describe.h       |  2 +-
 src/bin/psql/help.c           |  4 ++--
 src/include/utils/acl.h       |  1 +
 10 files changed, 117 insertions(+), 10 deletions(-)

diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 295e0b0..b5d23f3 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role is the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
@@ -1224,10 +1256,16 @@ GrantRole(GrantRoleStmt *stmt)
 	ListCell   *item;
 
 	if (stmt->grantor)
+	{
+		check_rolespec_name(stmt->grantor);
 		grantor = get_rolespec_oid(stmt->grantor, false);
+	}
 	else
 		grantor = GetUserId();
 
+	foreach(item, stmt->grantee_roles)
+		check_rolespec_name(lfirst(item));
+
 	grantee_ids = roleSpecsToIds(stmt->grantee_roles);
 
 	/* AccessShareLock is enough since we aren't modifying pg_authid */
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3ca168b..fdbe2d4 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
@@ -5247,3 +5248,28 @@ get_rolespec_name(const Node *node)
 
 	return rolename;
 }
+
+/*
+ * Given a RoleSpec, throw an error if the name is reserved.
+ */
+void
+check_rolespec_name(const Node *node)
+{
+	RoleSpec   *role;
+
+	role = (RoleSpec *) node;
+	if (!IsA(node, RoleSpec))
+		elog(ERROR, "invalid node type %d", node->type);
+
+	if (role->roletype != ROLESPEC_CSTRING)
+		return;
+
+	if (IsReservedName(role->rolename))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role \"%s\" is reserved",
+					 role->rolename),
+				 errdetail("Roles starting with \"pg_\" are reserved.")));
+
+	return;
+}
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 41d4606..d115b2a 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 72c00c1..3e7cc3f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -431,7 +431,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -476,7 +476,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 92ed6e2..488693a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2647,7 +2647,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2692,6 +2692,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5b63e76..ff60a85 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 915ea39..7bad9fe 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -231,6 +231,7 @@ extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid	get_role_oid(const char *rolename, bool missing_ok);
 extern Oid	get_role_oid_or_public(const char *rolename);
 extern Oid	get_rolespec_oid(const Node *node, bool missing_ok);
+extern void	check_rolespec_name(const Node *node);
 extern HeapTuple get_rolespec_tuple(const Node *node);
 extern char *get_rolespec_name(const Node *node);
 
-- 
2.5.0


From d24fb4d2d74cfba8519b3d63a924e73f60ad859d Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:08:03 -0400
Subject: [PATCH 3/3] Create default roles

---
 contrib/test_decoding/expected/permissions.out |   8 +-
 doc/src/sgml/func.sgml                         |  23 ++---
 doc/src/sgml/user-manag.sgml                   |  83 +++++++++++++++++
 src/backend/access/transam/xlogfuncs.c         |  30 ++++---
 src/backend/catalog/system_views.sql           |   2 +
 src/backend/replication/logical/logicalfuncs.c |  17 ++--
 src/backend/replication/slotfuncs.c            |  29 +++---
 src/backend/replication/walsender.c            |   8 +-
 src/backend/utils/adt/misc.c                   |  12 +--
 src/backend/utils/adt/pgstatfuncs.c            |  25 ++++--
 src/backend/utils/misc/guc.c                   |   7 ++
 src/include/catalog/pg_authid.h                |  20 ++++-
 src/test/regress/expected/privileges.out       | 120 +++++++++++++++++++++++++
 src/test/regress/expected/rolenames.out        |  18 ++++
 src/test/regress/sql/privileges.sql            |  78 ++++++++++++++++
 src/test/regress/sql/rolenames.sql             |   8 ++
 16 files changed, 423 insertions(+), 65 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 60b9a09..5c110cc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16678,7 +16678,8 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Cancel a backend's current query.  This is also allowed if the
-        calling role is a member of the role whose backend is being canceled,
+        calling role is a member of the role whose backend is being canceled or
+        the calling role has been granted <literal>pg_signal_backend</literal>,
         however only superusers can cancel superuser backends.
         </entry>
       </row>
@@ -16702,8 +16703,9 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Terminate a backend.  This is also allowed if the calling role
-        is a member of the role whose backend is being terminated, however only
-        superusers can terminate superuser backends.
+        is a member of the role whose backend is being terminated or the
+        calling role has been granted <literal>pg_signal_backend</literal>,
+        however only superusers can terminate superuser backends.
        </entry>
       </row>
      </tbody>
@@ -16807,7 +16809,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Create a named point for performing restore (restricted to superusers)</entry>
+       <entry>Create a named point for performing restore (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16828,14 +16830,14 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Prepare for performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Finish performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16856,7 +16858,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
+       <entry>Force switch to a new transaction log file (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -17112,7 +17114,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_pause()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Pauses recovery immediately (restricted to superusers).
+       <entry>Pauses recovery immediately (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
       <row>
@@ -17120,7 +17122,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_resume()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Restarts recovery if it was paused (restricted to superusers).
+       <entry>Restarts recovery if it was paused (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
      </tbody>
@@ -17230,7 +17232,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
     See <xref linkend="streaming-replication">,
     <xref linkend="streaming-replication-slots">, <xref linkend="replication-origins">
     for information about the underlying features.  Use of these
-    functions is restricted to superusers.
+    functions is restricted to superuser or <literal>pg_replication</literal>
+    roles.
    </para>
 
    <para>
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 1952cac..89ac5c2 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -472,6 +472,89 @@ DROP ROLE doomed_role;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch to a new transaction log file, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_monitor</entry>
+       <entry>To privileged system information (eg: activity of other users, replication lag)</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume xlog replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Request logfile rotation</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate)</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all configuration files</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..dda39ad 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +145,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -348,10 +354,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +376,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ccc030f..7457f98 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -418,6 +418,8 @@ CREATE VIEW pg_file_settings AS
 
 REVOKE ALL on pg_file_settings FROM PUBLIC;
 REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC;
+GRANT SELECT on pg_file_settings TO pg_file_settings;
+GRANT EXECUTE ON FUNCTION pg_show_all_file_settings() TO pg_file_settings;
 
 CREATE VIEW pg_timezone_abbrevs AS
     SELECT * FROM pg_timezone_abbrevs();
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 012987a..968ad93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4a4643e..bf0194c 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2808,11 +2810,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index 3ef6e43..c320966 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being canceled"))));
+				 (errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..e28eae3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8249,6 +8251,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all config file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index 2c8565e..693f0b6 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -93,10 +93,28 @@ typedef FormData_pg_authid *Form_pg_authid;
  *
  * The uppercase quantities will be replaced at initdb time with
  * user choices.
+ *
+ * If adding new default roles or changing the OIDs below, be sure to add or
+ * update the #defines which follow as appropriate.
  * ----------------
  */
 DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 4201 ( "pg_monitor" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4202 ( "pg_backup" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4203 ( "pg_replay" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4204 ( "pg_replication" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4205 ( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4206 ( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4207 ( "pg_file_settings" f t f f f f f -1 _null_ _null_));
+
+#define BOOTSTRAP_SUPERUSERID			10
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define DEFAULT_ROLE_MONITORID			4201
+#define DEFAULT_ROLE_BACKUPID			4202
+#define DEFAULT_ROLE_REPLAYID			4203
+#define DEFAULT_ROLE_REPLICATIONID		4204
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	4205
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	4206
+#define DEFAULT_ROLE_FILE_SETTINGSID	4207
 
 #endif   /* PG_AUTHID_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 88bdc2c..7e02b36 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1523,6 +1523,126 @@ revoke select on dep_priv_test from regressuser4 cascade;
 
 set session role regressuser1;
 drop table dep_priv_test;
+-- test default roles
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup to switch transaction log files
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or "logical" at server start.
+SELECT pg_stop_backup(); -- fail
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or "logical" at server start.
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+\c
+REVOKE pg_backup FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup to switch transaction log files
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for function pg_show_all_file_settings
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for relation pg_file_settings
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+\c
+REVOKE pg_file_settings FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for function pg_show_all_file_settings
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for relation pg_file_settings
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+SELECT pg_xlog_replay_resume(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+\c
+REVOKE pg_replay FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail (not a permission failure)
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or "logical" at server start.
+SELECT pg_stop_backup(); -- fail
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or "logical" at server start.
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail
+ERROR:  replication slots can only be used if max_replication_slots > 0
+SELECT pg_drop_replication_slot('asd'); -- fail
+ERROR:  replication slots can only be used if max_replication_slots > 0
+\c
+REVOKE pg_replication FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
 -- clean up
 \c
 drop sequence x_seq;
diff --git a/src/test/regress/expected/rolenames.out b/src/test/regress/expected/rolenames.out
index 8f88c02..99e6aec 100644
--- a/src/test/regress/expected/rolenames.out
+++ b/src/test/regress/expected/rolenames.out
@@ -78,6 +78,18 @@ CREATE ROLE "none"; -- error
 ERROR:  role name "none" is reserved
 LINE 1: CREATE ROLE "none";
                     ^
+CREATE ROLE pg_abc; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abc"; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE pg_backup; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_backup"; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -804,6 +816,12 @@ LINE 1: DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9;
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 NOTICE:  role "nonexistent" does not exist, skipping
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
+DETAIL:  Roles starting with "pg_" are reserved.
+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Roles starting with "pg_" are reserved.
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
  proname  | proacl 
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c1837c4..856f5ec 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -932,6 +932,84 @@ revoke select on dep_priv_test from regressuser4 cascade;
 set session role regressuser1;
 drop table dep_priv_test;
 
+-- test default roles
+
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT pg_switch_xlog(); -- fail-no-perm
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail
+SELECT pg_stop_backup(); -- fail
+SELECT pg_switch_xlog() limit 0; -- success
+\c
+REVOKE pg_backup FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT pg_switch_xlog(); -- fail-no-perm
+
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+\c
+REVOKE pg_file_settings FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+SELECT pg_xlog_replay_resume(); -- fail
+\c
+REVOKE pg_replay FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail (not a permission failure)
+SELECT pg_stop_backup(); -- fail
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail
+SELECT pg_drop_replication_slot('asd'); -- fail
+\c
+REVOKE pg_replication FROM regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
 
 -- clean up
 
diff --git a/src/test/regress/sql/rolenames.sql b/src/test/regress/sql/rolenames.sql
index e8c6b33..65c97ec 100644
--- a/src/test/regress/sql/rolenames.sql
+++ b/src/test/regress/sql/rolenames.sql
@@ -57,6 +57,11 @@ CREATE ROLE "public"; -- error
 CREATE ROLE none; -- error
 CREATE ROLE "none"; -- error
 
+CREATE ROLE pg_abc; -- error
+CREATE ROLE "pg_abc"; -- error
+CREATE ROLE pg_backup; -- error
+CREATE ROLE "pg_backup"; -- error
+
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -376,6 +381,9 @@ DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+GRANT pg_backup TO pg_monitor; -- error
+
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
 
-- 
2.5.0

#119Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#118)
Re: Additional role attributes && superuser review

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

I'm guessing you were referring to pg_admin with your comment that you
were "not convinced that we actually need it." After thinking about
it for a bit, I tend to agree. A user could certainly create their own
role which combines these all together if they wanted to (or do any
other combinations, based on their particular needs). I've gone ahead
and removed pg_admin from the set of default roles.

Yeah that 's what I meant. Thanks, I should have been more precise
with my words.

+       if (IsReservedName(stmt->role))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        stmt->role),
+                                errdetail("Role names starting with
\"pg_\" are reserved.")));
Perhaps this could be a separate change? It seems that restricting roles
with pg_ on the cluster is not a bad restriction in any case. You may want
to add regression tests to trigger those errors as well.

I'm a bit confused- this is a separate change. Note that the previous
patch was actually a git patchset which had two patches- one to do the
reservation and a second to add the default roles. The attached
patchset is actually three patches:
1- Update to catalog documentation regarding permissions
2- Reserve 'pg_' role namespace
3- Add default roles

I see, that's why I got confused. I just downloaded your file and
applied it blindly with git apply or patch (don't recall which). Other
folks tend to publish that as a separate set of files generated by
git-format-patch.

Shouldn't pg_current_xlog_location, pg_current_xlog_insert_location,
pg_last_xlog_receive_location and pg_last_xlog_replay_location fall under a
restriction category like pg_monitor? I recall of course that we discussed
that some months ago and that a lot of people were reluctant to harden this
area to not break any existing monitoring tool, still this patch may be the
occasion to restrict a bit those functions, particularly on servers where
wal_compression is enabled.

For my 2c, I believe they should. I've not modified them in that way in
this patchset, but I can certainly do so if others agree.

My vote goes into hardening them a bit this way.

I've added regression tests for the default roles where it was
relatively straight-forward to do so. I don't see a trivial way to test
that the pg_signal_backend role works though, as an example.

It is at least possible to check that the error code paths are
working. For the code paths where a backend would be actually running,
you could use the following:
SET client_min_messages TO 'error';
-- This should return "1" and not an ERROR nor a WARNING if PID does not exist.
select count(pg_terminate_backend(0));
But that's ugly depending on your taste.

Bonus:
-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"))));
-}
I would have let check_permissions and directly done the checks on
has_rolreplication and has_privs_of_role in it, the same code being
duplicated three times.

For my 2c, I dislike the notion of "check_permissions()" functions being
added throughout the codebase as I'm afraid it'd get confusing, which is
why I got rid of it. I'm much happier seeing the actual permissions
check as it should be happening early on in the primary function which
is being called into. If there is really a push to go back to having a
'check_permissions()' like function, I'd argue that we should make the
function name more descriptive of what's actually being done and have
them be distinct from each other. As I recall, I discussed this change
with Andres and he didn't feel particularly strongly about this one way
or the other, therefore, I've not made this change for this patchset.

Fine for me. Usually people point out such code duplications are bad
things, and here we just have a static routine being used for a set of
functions interacting with the same kind of object, here a replication
slot. But I'm fine either way.

Do you think that it makes sense to add tests as well in the second
patch to check that restrictions pg_* are in place? Except that I
don't have much more to say about patches 1 and 2 which are rather
straight-forward.

Regarding patch 3, the documentation of psql should mention the new
subommands \dgS and \dgS+. That's a one-liner.

+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or
"logical" at server start.
make install would wal on a server with something else than wal_level
= minimal. Just checking that errors kick correctly looks fine to me
here.
+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Roles starting with "pg_" are reserved.
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
We would gain with verbose error messages here, like, "cannot GRANT
somerole to a system role", and "cannot GRANT system role to
somerole".
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#120David Steele
david@pgmasters.net
In reply to: Michael Paquier (#119)
Re: Additional role attributes && superuser review

On 11/19/15 2:13 AM, Michael Paquier wrote:

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

I wouldn't say that backup and archiving are unrelated since backups
aren't valid without the proper set of WAL segments.

When configuring archiving/backup I use pg_switch_xlog() to verify that
WAL segments are getting to the archive.

I also use pg_switch_xlog() after pg_create_restore_point() to force the
WAL segment containing the restore point record to the archive.
Otherwise it might not be possible (or at least not easy) to recover to
the restore point in case of a problem. The required WAL segment is
likely to get pushed to the archive before it is needed but I would
rather not bet on that.

--
-David
david@pgmasters.net

#121Stephen Frost
sfrost@snowman.net
In reply to: David Steele (#120)
1 attachment(s)
Re: Additional role attributes && superuser review

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

As David notes, they're actually quite related. Note that in our
documentation pg_switch_xlog() is listed in the "Backup Control
Functions" table.

I can think of a use-case for a user who can call pg_switch_xlog, but
not pg_start_backup()/pg_stop_backup(), but I have to admit that it
seems rather limited and I'm on the fence about it being a worthwhile
distinction.

Even so, in the interest of having more fine-grained permission
controls, I've gone ahead and added a pg_switch_xlog default role.
Note that this means that pg_switch_xlog() can be called by both
pg_switch_xlog roles and pg_backup roles. I'd be very much against
removing the ability to call pg_switch_xlog from the pg_backup role as
that really is a capability which is needed by users running backups and
it'd just add unnecessary complexity to require users setting up backup
tools to grant two different roles to get the backup to work.

I'm a bit confused- this is a separate change. Note that the previous
patch was actually a git patchset which had two patches- one to do the
reservation and a second to add the default roles. The attached
patchset is actually three patches:
1- Update to catalog documentation regarding permissions
2- Reserve 'pg_' role namespace
3- Add default roles

I see, that's why I got confused. I just downloaded your file and
applied it blindly with git apply or patch (don't recall which). Other
folks tend to publish that as a separate set of files generated by
git-format-patch.

The file was generated using 'git format-patch', but sent to one file
instead of multiple. 'git am' understands that format and will add the
independent commits to the current branch. Note that the git
documentation (see 'man git-format-patch' and 'man git-apply', at least
on Debian-based systems) specifically recommends using 'git am' to
create commits from patches generated by 'git format-patch'.

The workflow you're describing would require saving off each patch,
doing a 'patch' or 'git apply' run for each, then committing the changes
with your own commit message and only then would you have the entire
series. That doesn't seem like a better approach.

Shouldn't pg_current_xlog_location, pg_current_xlog_insert_location,
pg_last_xlog_receive_location and pg_last_xlog_replay_location fall under a
restriction category like pg_monitor? I recall of course that we discussed
that some months ago and that a lot of people were reluctant to harden this
area to not break any existing monitoring tool, still this patch may be the
occasion to restrict a bit those functions, particularly on servers where
wal_compression is enabled.

For my 2c, I believe they should. I've not modified them in that way in
this patchset, but I can certainly do so if others agree.

My vote goes into hardening them a bit this way.

I've made those changes in this patch set. Note that these functions
can now only be called by roles which are members of pg_monitor,
pg_backup, or pg_switch_xlog (or superuser, of course).

I've added regression tests for the default roles where it was
relatively straight-forward to do so. I don't see a trivial way to test
that the pg_signal_backend role works though, as an example.

It is at least possible to check that the error code paths are
working.

Perhaps, but...

For the code paths where a backend would be actually running,
you could use the following:
SET client_min_messages TO 'error';
-- This should return "1" and not an ERROR nor a WARNING if PID does not exist.
select count(pg_terminate_backend(0));
But that's ugly depending on your taste.

I really dislike that.

Do you think that it makes sense to add tests as well in the second
patch to check that restrictions pg_* are in place? Except that I
don't have much more to say about patches 1 and 2 which are rather
straight-forward.

Ah, yes. I've now moved those hunks to the second patch where they
really should have been.

Regarding patch 3, the documentation of psql should mention the new
subommands \dgS and \dgS+. That's a one-liner.

Ah, right. I've updated the psql SGML documentation for \duS and \dgS.
The '\h' output had already been updated. Was there something else
which you meant above that I've missed? Note that these fixes went into
the second patch.

+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or
"logical" at server start.
make install would wal on a server with something else than wal_level
= minimal. Just checking that errors kick correctly looks fine to me
here.

I've removed those checks as they could get annoying on the buildfarm or
for people doing make installcheck, as you say, but I'm not really
thrilled that we're only testing the failure paths.

+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Roles starting with "pg_" are reserved.
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
We would gain with verbose error messages here, like, "cannot GRANT
somerole to a system role", and "cannot GRANT system role to
somerole".

Alright, I've changed these to have better detail messages.

Updated patchset attached.

Thanks!

Stephen

Attachments:

default_roles_v8.patchtext/x-diff; charset=us-asciiDownload
From 141fce786f3fdb7e5cb16d9aaa0c81f8b6caa6d7 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 18 Nov 2015 11:50:57 -0500
Subject: [PATCH 1/3] Add note regarding permissions in pg_catalog

Add a note to the system catalog section pointing out that while
modifying the permissions on catalog tables is possible, it's
unlikely to have the desired effect.
---
 doc/src/sgml/catalogs.sgml | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 97ef618..3b7768c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,17 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <note>
+   <para>
+    Changing the permissions on objects in the system catalogs, while
+    possible, is unlikely to have the desired effect as the internal
+    lookup functions use a cache and do not check the permissions nor
+    policies of tables in the system catalog.  Further, permission
+    changes to objects in the system catalogs are not preserved by
+    pg_dump or across upgrades.
+   </para>
+  </note>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
-- 
2.5.0


From 0a5df0084d4411e82835be67606bbd0109cee1e6 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:04:55 -0400
Subject: [PATCH 2/3] Reserve the "pg_" namespace for roles

This will prevent users from creating roles which being with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.
---
 doc/src/sgml/ref/psql-ref.sgml          |  8 +++++--
 src/backend/catalog/catalog.c           |  5 +++--
 src/backend/commands/user.c             | 40 +++++++++++++++++++++++++++++++++
 src/backend/utils/adt/acl.c             | 39 ++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dumpall.c            |  2 ++
 src/bin/pg_upgrade/check.c              | 40 +++++++++++++++++++++++++++++++--
 src/bin/psql/command.c                  |  4 ++--
 src/bin/psql/describe.c                 |  5 ++++-
 src/bin/psql/describe.h                 |  2 +-
 src/bin/psql/help.c                     |  4 ++--
 src/include/utils/acl.h                 |  1 +
 src/test/regress/expected/rolenames.out | 18 +++++++++++++++
 src/test/regress/sql/rolenames.sql      |  8 +++++++
 13 files changed, 164 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 5899bb4..9f6742b 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1336,13 +1336,15 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\dg[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dgS[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\du</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\dg+</literal> is used, additional information
@@ -1496,13 +1498,15 @@ testdb=&gt;
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\duS[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\dg</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\du+</literal> is used, additional information
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 295e0b0..22c8d3e 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role is the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
@@ -1224,10 +1256,18 @@ GrantRole(GrantRoleStmt *stmt)
 	ListCell   *item;
 
 	if (stmt->grantor)
+	{
+		check_rolespec_name(stmt->grantor,
+							"Cannot specify reserved role as grantor.");
 		grantor = get_rolespec_oid(stmt->grantor, false);
+	}
 	else
 		grantor = GetUserId();
 
+	foreach(item, stmt->grantee_roles)
+		check_rolespec_name(lfirst(item),
+							"Cannot GRANT roles to a reserved role.");
+
 	grantee_ids = roleSpecsToIds(stmt->grantee_roles);
 
 	/* AccessShareLock is enough since we aren't modifying pg_authid */
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3ca168b..be4ad45 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
@@ -5247,3 +5248,41 @@ get_rolespec_name(const Node *node)
 
 	return rolename;
 }
+
+/*
+ * Given a RoleSpec, throw an error if the name is reserved, using errdetail.
+ *
+ * Node and errdetail must be provided.
+ */
+void
+check_rolespec_name(const Node *node, const char *detail_msg)
+{
+	RoleSpec   *role;
+
+	if (!node)
+		return;
+
+	role = (RoleSpec *) node;
+	if (!IsA(node, RoleSpec))
+		elog(ERROR, "invalid node type %d", node->type);
+
+	if (role->roletype != ROLESPEC_CSTRING)
+		return;
+
+	if (IsReservedName(role->rolename))
+	{
+		if (detail_msg)
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename),
+					 errdetail("%s", detail_msg)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename)));
+	}
+
+	return;
+}
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 41d4606..d115b2a 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 72c00c1..3e7cc3f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -431,7 +431,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -476,7 +476,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 92ed6e2..488693a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2647,7 +2647,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2692,6 +2692,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5b63e76..ff60a85 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 915ea39..5fccd88 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -231,6 +231,7 @@ extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid	get_role_oid(const char *rolename, bool missing_ok);
 extern Oid	get_role_oid_or_public(const char *rolename);
 extern Oid	get_rolespec_oid(const Node *node, bool missing_ok);
+extern void	check_rolespec_name(const Node *node, const char *detail_msg);
 extern HeapTuple get_rolespec_tuple(const Node *node);
 extern char *get_rolespec_name(const Node *node);
 
diff --git a/src/test/regress/expected/rolenames.out b/src/test/regress/expected/rolenames.out
index 8f88c02..c9be282 100644
--- a/src/test/regress/expected/rolenames.out
+++ b/src/test/regress/expected/rolenames.out
@@ -78,6 +78,18 @@ CREATE ROLE "none"; -- error
 ERROR:  role name "none" is reserved
 LINE 1: CREATE ROLE "none";
                     ^
+CREATE ROLE pg_abc; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abc"; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE pg_backup; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_backup"; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -804,6 +816,12 @@ LINE 1: DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9;
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 NOTICE:  role "nonexistent" does not exist, skipping
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
  proname  | proacl 
diff --git a/src/test/regress/sql/rolenames.sql b/src/test/regress/sql/rolenames.sql
index e8c6b33..65c97ec 100644
--- a/src/test/regress/sql/rolenames.sql
+++ b/src/test/regress/sql/rolenames.sql
@@ -57,6 +57,11 @@ CREATE ROLE "public"; -- error
 CREATE ROLE none; -- error
 CREATE ROLE "none"; -- error
 
+CREATE ROLE pg_abc; -- error
+CREATE ROLE "pg_abc"; -- error
+CREATE ROLE pg_backup; -- error
+CREATE ROLE "pg_backup"; -- error
+
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -376,6 +381,9 @@ DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+GRANT pg_backup TO pg_monitor; -- error
+
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
 
-- 
2.5.0


From 7f5ffcb6940a859e485807345ebe82f34d5780a0 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:08:03 -0400
Subject: [PATCH 3/3] Create default roles

---
 contrib/test_decoding/expected/permissions.out |   8 +-
 doc/src/sgml/func.sgml                         |  23 +--
 doc/src/sgml/user-manag.sgml                   |  83 +++++++++++
 src/backend/access/transam/xlogfuncs.c         |  59 ++++++--
 src/backend/catalog/system_views.sql           |   2 +
 src/backend/replication/logical/logicalfuncs.c |  17 +--
 src/backend/replication/slotfuncs.c            |  29 ++--
 src/backend/replication/walsender.c            |   8 +-
 src/backend/utils/adt/misc.c                   |  12 +-
 src/backend/utils/adt/pgstatfuncs.c            |  25 ++--
 src/backend/utils/misc/guc.c                   |   7 +
 src/include/catalog/pg_authid.h                |  22 ++-
 src/test/regress/expected/privileges.out       | 185 +++++++++++++++++++++++++
 src/test/regress/sql/privileges.sql            | 103 ++++++++++++++
 14 files changed, 518 insertions(+), 65 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 60b9a09..5c110cc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16678,7 +16678,8 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Cancel a backend's current query.  This is also allowed if the
-        calling role is a member of the role whose backend is being canceled,
+        calling role is a member of the role whose backend is being canceled or
+        the calling role has been granted <literal>pg_signal_backend</literal>,
         however only superusers can cancel superuser backends.
         </entry>
       </row>
@@ -16702,8 +16703,9 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Terminate a backend.  This is also allowed if the calling role
-        is a member of the role whose backend is being terminated, however only
-        superusers can terminate superuser backends.
+        is a member of the role whose backend is being terminated or the
+        calling role has been granted <literal>pg_signal_backend</literal>,
+        however only superusers can terminate superuser backends.
        </entry>
       </row>
      </tbody>
@@ -16807,7 +16809,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Create a named point for performing restore (restricted to superusers)</entry>
+       <entry>Create a named point for performing restore (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16828,14 +16830,14 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Prepare for performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Finish performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16856,7 +16858,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
+       <entry>Force switch to a new transaction log file (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -17112,7 +17114,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_pause()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Pauses recovery immediately (restricted to superusers).
+       <entry>Pauses recovery immediately (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
       <row>
@@ -17120,7 +17122,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_resume()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Restarts recovery if it was paused (restricted to superusers).
+       <entry>Restarts recovery if it was paused (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
      </tbody>
@@ -17230,7 +17232,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
     See <xref linkend="streaming-replication">,
     <xref linkend="streaming-replication-slots">, <xref linkend="replication-origins">
     for information about the underlying features.  Use of these
-    functions is restricted to superusers.
+    functions is restricted to superuser or <literal>pg_replication</literal>
+    roles.
    </para>
 
    <para>
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 1952cac..89ac5c2 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -472,6 +472,89 @@ DROP ROLE doomed_role;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch to a new transaction log file, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_monitor</entry>
+       <entry>To privileged system information (eg: activity of other users, replication lag)</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume xlog replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Request logfile rotation</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate)</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>Allowed to view config settings from all configuration files</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..5819377 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,11 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup or pg_switch_xlog to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +146,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -183,6 +190,13 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -204,6 +218,13 @@ pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -226,6 +247,13 @@ pg_last_xlog_receive_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files")));
+
 	recptr = GetWalRcvWriteRecPtr(NULL, NULL);
 
 	if (recptr == 0)
@@ -245,6 +273,13 @@ pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files")));
+
 	recptr = GetXLogReplayRecPtr(NULL);
 
 	if (recptr == 0)
@@ -348,10 +383,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +405,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ccc030f..7457f98 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -418,6 +418,8 @@ CREATE VIEW pg_file_settings AS
 
 REVOKE ALL on pg_file_settings FROM PUBLIC;
 REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC;
+GRANT SELECT on pg_file_settings TO pg_file_settings;
+GRANT EXECUTE ON FUNCTION pg_show_all_file_settings() TO pg_file_settings;
 
 CREATE VIEW pg_timezone_abbrevs AS
     SELECT * FROM pg_timezone_abbrevs();
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 012987a..968ad93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4a4643e..bf0194c 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2808,11 +2810,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index 3ef6e43..c320966 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being canceled"))));
+				 (errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..e28eae3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8249,6 +8251,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all config file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index 2c8565e..d285536 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -93,10 +93,30 @@ typedef FormData_pg_authid *Form_pg_authid;
  *
  * The uppercase quantities will be replaced at initdb time with
  * user choices.
+ *
+ * If adding new default roles or changing the OIDs below, be sure to add or
+ * update the #defines which follow as appropriate.
  * ----------------
  */
 DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 4200 ( "pg_monitor" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4201 ( "pg_backup" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4202 ( "pg_replay" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4203 ( "pg_replication" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4204 ( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4205 ( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4206 ( "pg_file_settings" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4207 ( "pg_switch_xlog" f t f f f f f -1 _null_ _null_));
+
+#define BOOTSTRAP_SUPERUSERID			10
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define DEFAULT_ROLE_MONITORID			4200
+#define DEFAULT_ROLE_BACKUPID			4201
+#define DEFAULT_ROLE_REPLAYID			4202
+#define DEFAULT_ROLE_REPLICATIONID		4203
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	4204
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	4205
+#define DEFAULT_ROLE_FILE_SETTINGSID	4206
+#define DEFAULT_ROLE_SWITCH_XLOGID		4207
 
 #endif   /* PG_AUTHID_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 88bdc2c..1a5c1ae 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1523,6 +1523,191 @@ revoke select on dep_priv_test from regressuser4 cascade;
 
 set session role regressuser1;
 drop table dep_priv_test;
+-- test default roles
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_backup FROM regressuser1;
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for function pg_show_all_file_settings
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for relation pg_file_settings
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+\c
+REVOKE pg_file_settings FROM regressuser1;
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_monitor FROM regressuser1;
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+SELECT pg_xlog_replay_resume(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+\c
+REVOKE pg_replay FROM regressuser1;
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
+-- pg_switch_xlog
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log files
+\c
+GRANT pg_switch_xlog TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_switch_xlog FROM regressuser1;
 -- clean up
 \c
 drop sequence x_seq;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c1837c4..9b905a5 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -932,6 +932,109 @@ revoke select on dep_priv_test from regressuser4 cascade;
 set session role regressuser1;
 drop table dep_priv_test;
 
+-- test default roles
+
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT pg_switch_xlog(); -- fail-no-perm
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_backup FROM regressuser1;
+
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+\c
+REVOKE pg_file_settings FROM regressuser1;
+
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_monitor FROM regressuser1;
+
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+SELECT pg_xlog_replay_resume(); -- fail
+\c
+REVOKE pg_replay FROM regressuser1;
+
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
+
+-- pg_switch_xlog
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_switch_xlog(); -- fail-no-perm
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_switch_xlog TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_switch_xlog FROM regressuser1;
 
 -- clean up
 
-- 
2.5.0

#122Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#121)
Re: Additional role attributes && superuser review

On Sat, Nov 21, 2015 at 2:29 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:
Even so, in the interest of having more fine-grained permission
controls, I've gone ahead and added a pg_switch_xlog default role.
Note that this means that pg_switch_xlog() can be called by both
pg_switch_xlog roles and pg_backup roles. I'd be very much against
removing the ability to call pg_switch_xlog from the pg_backup role as
that really is a capability which is needed by users running backups and
it'd just add unnecessary complexity to require users setting up backup
tools to grant two different roles to get the backup to work.

There is going to be many opinions regarding the granularity of this
control, each one of us having a different opinion at the end. I don't
think this should be a stopper for this patch, hence I am fine with the
judgement you think is good. We could still more finely tune those default
roles later in the dev cycle of 9.6 (10.0?).

For the code paths where a backend would be actually running,
you could use the following:
SET client_min_messages TO 'error';
-- This should return "1" and not an ERROR nor a WARNING if PID does not

exist.

select count(pg_terminate_backend(0));
But that's ugly depending on your taste.

I really dislike that.

Do you think that it makes sense to add tests as well in the second
patch to check that restrictions pg_* are in place? Except that I
don't have much more to say about patches 1 and 2 which are rather
straight-forward.

Ah, yes. I've now moved those hunks to the second patch where they
really should have been.

Regarding patch 3, the documentation of psql should mention the new
subommands \dgS and \dgS+. That's a one-liner.

Ah, right. I've updated the psql SGML documentation for \duS and \dgS.
The '\h' output had already been updated. Was there something else
which you meant above that I've missed? Note that these fixes went into
the second patch.

Thanks, this looks good to me.

+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail
+ERROR:  WAL level not sufficient for making an online backup
+HINT:  wal_level must be set to "archive", "hot_standby", or
"logical" at server start.
make install would wal on a server with something else than wal_level
= minimal. Just checking that errors kick correctly looks fine to me
here.

I've removed those checks as they could get annoying on the buildfarm or
for people doing make installcheck, as you say, but I'm not really
thrilled that we're only testing the failure paths.

I guess that's better than nothing.

+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Roles starting with "pg_" are reserved.
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
We would gain with verbose error messages here, like, "cannot GRANT
somerole to a system role", and "cannot GRANT system role to
somerole".

Alright, I've changed these to have better detail messages.

@@ -183,6 +190,13 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
{
XLogRecPtr current_recptr;

+       if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+               !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+               !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser or member of
pg_monitor, pg_backup, or pg_switch_xlog to switch transaction log
files")));

I don't think you mean to refer to the switch of segments files here. Same
comment for pg_current_xlog_insert_location, pg_last_xlog_receive_location
and pg_last_xlog_replay_location.

+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser or member of
pg_file_settings to see all config file settings")));
Should avoid abbreviations => "all configuration file settings".

<varlistentry>
- <term><literal>\dg[+] [ <link
linkend="APP-PSQL-patterns"><replaceable
class="parameter">pattern</replaceable></link> ]</literal></term>
+ <term><literal>\dgS[+] [ <link
linkend="APP-PSQL-patterns"><replaceable
class="parameter">pattern</replaceable></link> ]</literal></term>
<listitem>
I'm picky here, but that should be "\dg[S+]". Same for \du[S+].

The rest looks fine.
Regards,
--
Michael

#123Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#122)
1 attachment(s)
Re: Additional role attributes && superuser review

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Sat, Nov 21, 2015 at 2:29 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:
Even so, in the interest of having more fine-grained permission
controls, I've gone ahead and added a pg_switch_xlog default role.
Note that this means that pg_switch_xlog() can be called by both
pg_switch_xlog roles and pg_backup roles. I'd be very much against
removing the ability to call pg_switch_xlog from the pg_backup role as
that really is a capability which is needed by users running backups and
it'd just add unnecessary complexity to require users setting up backup
tools to grant two different roles to get the backup to work.

There is going to be many opinions regarding the granularity of this
control, each one of us having a different opinion at the end. I don't
think this should be a stopper for this patch, hence I am fine with the
judgement you think is good. We could still more finely tune those default
roles later in the dev cycle of 9.6 (10.0?).

Agreed.

Thanks, this looks good to me.

Great.

I guess that's better than nothing.

Agreed.

I don't think you mean to refer to the switch of segments files here. Same
comment for pg_current_xlog_insert_location, pg_last_xlog_receive_location
and pg_last_xlog_replay_location.

Urgh. Got a bit ahead of myself there, apologies. I've updated all of
these and a number of other minor typos and incorrect comments along the
way.

+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser or member of
pg_file_settings to see all config file settings")));
Should avoid abbreviations => "all configuration file settings".

Fixed.

<varlistentry>
- <term><literal>\dg[+] [ <link
linkend="APP-PSQL-patterns"><replaceable
class="parameter">pattern</replaceable></link> ]</literal></term>
+ <term><literal>\dgS[+] [ <link
linkend="APP-PSQL-patterns"><replaceable
class="parameter">pattern</replaceable></link> ]</literal></term>
<listitem>
I'm picky here, but that should be "\dg[S+]". Same for \du[S+].

Fixed

Updated patch attached.

Thanks!

Stephen

Attachments:

default_roles_v9.patchtext/x-diff; charset=us-asciiDownload
From 37b4627352287328ad28cd167620f51c26a68f04 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 18 Nov 2015 11:50:57 -0500
Subject: [PATCH 1/3] Add note regarding permissions in pg_catalog

Add a note to the system catalog section pointing out that while
modifying the permissions on catalog tables is possible, it's
unlikely to have the desired effect.
---
 doc/src/sgml/catalogs.sgml | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 97ef618..3b7768c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,17 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <note>
+   <para>
+    Changing the permissions on objects in the system catalogs, while
+    possible, is unlikely to have the desired effect as the internal
+    lookup functions use a cache and do not check the permissions nor
+    policies of tables in the system catalog.  Further, permission
+    changes to objects in the system catalogs are not preserved by
+    pg_dump or across upgrades.
+   </para>
+  </note>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
-- 
2.5.0


From 408a8ee807edbe875ff9fd860ebfd6c859dcffb8 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:04:55 -0400
Subject: [PATCH 2/3] Reserve the "pg_" namespace for roles

This will prevent users from creating roles which begin with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.
---
 doc/src/sgml/ref/psql-ref.sgml          |  8 +++++--
 src/backend/catalog/catalog.c           |  5 ++--
 src/backend/commands/user.c             | 40 ++++++++++++++++++++++++++++++++
 src/backend/utils/adt/acl.c             | 41 +++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dumpall.c            |  2 ++
 src/bin/pg_upgrade/check.c              | 40 ++++++++++++++++++++++++++++++--
 src/bin/psql/command.c                  |  4 ++--
 src/bin/psql/describe.c                 |  5 +++-
 src/bin/psql/describe.h                 |  2 +-
 src/bin/psql/help.c                     |  4 ++--
 src/include/utils/acl.h                 |  1 +
 src/test/regress/expected/rolenames.out | 18 +++++++++++++++
 src/test/regress/sql/rolenames.sql      |  8 +++++++
 13 files changed, 166 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 5899bb4..e0de107 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1336,13 +1336,15 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\dg[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dg[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\du</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\dg+</literal> is used, additional information
@@ -1496,13 +1498,15 @@ testdb=&gt;
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\du[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\dg</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\du+</literal> is used, additional information
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 295e0b0..a810b1c 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role in the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
@@ -1224,10 +1256,18 @@ GrantRole(GrantRoleStmt *stmt)
 	ListCell   *item;
 
 	if (stmt->grantor)
+	{
+		check_rolespec_name(stmt->grantor,
+							"Cannot specify reserved role as grantor.");
 		grantor = get_rolespec_oid(stmt->grantor, false);
+	}
 	else
 		grantor = GetUserId();
 
+	foreach(item, stmt->grantee_roles)
+		check_rolespec_name(lfirst(item),
+							"Cannot GRANT roles to a reserved role.");
+
 	grantee_ids = roleSpecsToIds(stmt->grantee_roles);
 
 	/* AccessShareLock is enough since we aren't modifying pg_authid */
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3ca168b..eb153a3 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
@@ -5247,3 +5248,43 @@ get_rolespec_name(const Node *node)
 
 	return rolename;
 }
+
+/*
+ * Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
+ * if provided.
+ *
+ * If node is NULL, no error is thrown.  If detail_msg is NULL then no detail
+ * message is provided.
+ */
+void
+check_rolespec_name(const Node *node, const char *detail_msg)
+{
+	RoleSpec   *role;
+
+	if (!node)
+		return;
+
+	role = (RoleSpec *) node;
+	if (!IsA(node, RoleSpec))
+		elog(ERROR, "invalid node type %d", node->type);
+
+	if (role->roletype != ROLESPEC_CSTRING)
+		return;
+
+	if (IsReservedName(role->rolename))
+	{
+		if (detail_msg)
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename),
+					 errdetail("%s", detail_msg)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename)));
+	}
+
+	return;
+}
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 41d4606..d115b2a 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 72c00c1..3e7cc3f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -431,7 +431,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -476,7 +476,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 92ed6e2..488693a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2647,7 +2647,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2692,6 +2692,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5b63e76..ff60a85 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 915ea39..5fccd88 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -231,6 +231,7 @@ extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid	get_role_oid(const char *rolename, bool missing_ok);
 extern Oid	get_role_oid_or_public(const char *rolename);
 extern Oid	get_rolespec_oid(const Node *node, bool missing_ok);
+extern void	check_rolespec_name(const Node *node, const char *detail_msg);
 extern HeapTuple get_rolespec_tuple(const Node *node);
 extern char *get_rolespec_name(const Node *node);
 
diff --git a/src/test/regress/expected/rolenames.out b/src/test/regress/expected/rolenames.out
index 8f88c02..c9be282 100644
--- a/src/test/regress/expected/rolenames.out
+++ b/src/test/regress/expected/rolenames.out
@@ -78,6 +78,18 @@ CREATE ROLE "none"; -- error
 ERROR:  role name "none" is reserved
 LINE 1: CREATE ROLE "none";
                     ^
+CREATE ROLE pg_abc; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abc"; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE pg_backup; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_backup"; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -804,6 +816,12 @@ LINE 1: DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9;
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 NOTICE:  role "nonexistent" does not exist, skipping
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
  proname  | proacl 
diff --git a/src/test/regress/sql/rolenames.sql b/src/test/regress/sql/rolenames.sql
index e8c6b33..65c97ec 100644
--- a/src/test/regress/sql/rolenames.sql
+++ b/src/test/regress/sql/rolenames.sql
@@ -57,6 +57,11 @@ CREATE ROLE "public"; -- error
 CREATE ROLE none; -- error
 CREATE ROLE "none"; -- error
 
+CREATE ROLE pg_abc; -- error
+CREATE ROLE "pg_abc"; -- error
+CREATE ROLE pg_backup; -- error
+CREATE ROLE "pg_backup"; -- error
+
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -376,6 +381,9 @@ DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+GRANT pg_backup TO pg_monitor; -- error
+
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
 
-- 
2.5.0


From ae11b1e2d4167a0dca2e05d3c1edb3367f9f7a75 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:08:03 -0400
Subject: [PATCH 3/3] Create default roles

---
 contrib/test_decoding/expected/permissions.out |   8 +-
 doc/src/sgml/func.sgml                         |  23 +--
 doc/src/sgml/user-manag.sgml                   |  87 ++++++++++++
 src/backend/access/transam/xlogfuncs.c         |  59 ++++++--
 src/backend/catalog/system_views.sql           |   2 +
 src/backend/replication/logical/logicalfuncs.c |  17 +--
 src/backend/replication/slotfuncs.c            |  29 ++--
 src/backend/replication/walsender.c            |   8 +-
 src/backend/utils/adt/misc.c                   |  12 +-
 src/backend/utils/adt/pgstatfuncs.c            |  25 ++--
 src/backend/utils/misc/guc.c                   |   7 +
 src/include/catalog/pg_authid.h                |  22 ++-
 src/test/regress/expected/privileges.out       | 185 +++++++++++++++++++++++++
 src/test/regress/sql/privileges.sql            | 103 ++++++++++++++
 14 files changed, 522 insertions(+), 65 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 60b9a09..5c110cc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16678,7 +16678,8 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Cancel a backend's current query.  This is also allowed if the
-        calling role is a member of the role whose backend is being canceled,
+        calling role is a member of the role whose backend is being canceled or
+        the calling role has been granted <literal>pg_signal_backend</literal>,
         however only superusers can cancel superuser backends.
         </entry>
       </row>
@@ -16702,8 +16703,9 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Terminate a backend.  This is also allowed if the calling role
-        is a member of the role whose backend is being terminated, however only
-        superusers can terminate superuser backends.
+        is a member of the role whose backend is being terminated or the
+        calling role has been granted <literal>pg_signal_backend</literal>,
+        however only superusers can terminate superuser backends.
        </entry>
       </row>
      </tbody>
@@ -16807,7 +16809,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Create a named point for performing restore (restricted to superusers)</entry>
+       <entry>Create a named point for performing restore (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16828,14 +16830,14 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Prepare for performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Finish performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16856,7 +16858,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
+       <entry>Force switch to a new transaction log file (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -17112,7 +17114,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_pause()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Pauses recovery immediately (restricted to superusers).
+       <entry>Pauses recovery immediately (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
       <row>
@@ -17120,7 +17122,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_resume()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Restarts recovery if it was paused (restricted to superusers).
+       <entry>Restarts recovery if it was paused (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
      </tbody>
@@ -17230,7 +17232,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
     See <xref linkend="streaming-replication">,
     <xref linkend="streaming-replication-slots">, <xref linkend="replication-origins">
     for information about the underlying features.  Use of these
-    functions is restricted to superusers.
+    functions is restricted to superuser or <literal>pg_replication</literal>
+    roles.
    </para>
 
    <para>
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 1952cac..77307b6 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -472,6 +472,93 @@ DROP ROLE doomed_role;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch to a new transaction log file, view transaction log position information, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>View configuration settings from all configuration files.</entry>
+      </row>
+      <row>
+       <entry>pg_monitor</entry>
+       <entry>View privileged system information (eg: activity of other users, transaction log position information).</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume transaction log replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Request logfile rotation.</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate).</entry>
+      </row>
+      <row>
+       <entry>pg_switch_xlog</entry>
+       <entry>Switch transaction log file and view transaction log position position information.</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..fb79748 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,11 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup or pg_switch_xlog to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +146,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -183,6 +190,13 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog location")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -204,6 +218,13 @@ pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog insert location")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -226,6 +247,13 @@ pg_last_xlog_receive_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog receive location")));
+
 	recptr = GetWalRcvWriteRecPtr(NULL, NULL);
 
 	if (recptr == 0)
@@ -245,6 +273,13 @@ pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SWITCH_XLOGID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog replay location")));
+
 	recptr = GetXLogReplayRecPtr(NULL);
 
 	if (recptr == 0)
@@ -348,10 +383,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +405,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ccc030f..7457f98 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -418,6 +418,8 @@ CREATE VIEW pg_file_settings AS
 
 REVOKE ALL on pg_file_settings FROM PUBLIC;
 REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC;
+GRANT SELECT on pg_file_settings TO pg_file_settings;
+GRANT EXECUTE ON FUNCTION pg_show_all_file_settings() TO pg_file_settings;
 
 CREATE VIEW pg_timezone_abbrevs AS
     SELECT * FROM pg_timezone_abbrevs();
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 012987a..968ad93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4a4643e..bf0194c 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2808,11 +2810,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index 3ef6e43..c320966 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being canceled"))));
+				 (errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..4946903 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8249,6 +8251,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all configuration file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index 2c8565e..d285536 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -93,10 +93,30 @@ typedef FormData_pg_authid *Form_pg_authid;
  *
  * The uppercase quantities will be replaced at initdb time with
  * user choices.
+ *
+ * If adding new default roles or changing the OIDs below, be sure to add or
+ * update the #defines which follow as appropriate.
  * ----------------
  */
 DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 4200 ( "pg_monitor" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4201 ( "pg_backup" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4202 ( "pg_replay" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4203 ( "pg_replication" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4204 ( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4205 ( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4206 ( "pg_file_settings" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4207 ( "pg_switch_xlog" f t f f f f f -1 _null_ _null_));
+
+#define BOOTSTRAP_SUPERUSERID			10
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define DEFAULT_ROLE_MONITORID			4200
+#define DEFAULT_ROLE_BACKUPID			4201
+#define DEFAULT_ROLE_REPLAYID			4202
+#define DEFAULT_ROLE_REPLICATIONID		4203
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	4204
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	4205
+#define DEFAULT_ROLE_FILE_SETTINGSID	4206
+#define DEFAULT_ROLE_SWITCH_XLOGID		4207
 
 #endif   /* PG_AUTHID_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 88bdc2c..a083aae 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1523,6 +1523,191 @@ revoke select on dep_priv_test from regressuser4 cascade;
 
 set session role regressuser1;
 drop table dep_priv_test;
+-- test default roles
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog replay location
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_backup FROM regressuser1;
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for function pg_show_all_file_settings
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for relation pg_file_settings
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+\c
+REVOKE pg_file_settings FROM regressuser1;
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog replay location
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_monitor FROM regressuser1;
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+SELECT pg_xlog_replay_resume(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+\c
+REVOKE pg_replay FROM regressuser1;
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
+-- pg_switch_xlog
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_switch_xlog to switch transaction log files
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor, pg_backup, or pg_switch_xlog to view last xlog replay location
+\c
+GRANT pg_switch_xlog TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_switch_xlog FROM regressuser1;
 -- clean up
 \c
 drop sequence x_seq;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c1837c4..9b905a5 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -932,6 +932,109 @@ revoke select on dep_priv_test from regressuser4 cascade;
 set session role regressuser1;
 drop table dep_priv_test;
 
+-- test default roles
+
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT pg_switch_xlog(); -- fail-no-perm
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_backup FROM regressuser1;
+
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+\c
+REVOKE pg_file_settings FROM regressuser1;
+
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_monitor FROM regressuser1;
+
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+SELECT pg_xlog_replay_resume(); -- fail
+\c
+REVOKE pg_replay FROM regressuser1;
+
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
+
+-- pg_switch_xlog
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_switch_xlog(); -- fail-no-perm
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_switch_xlog TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_switch_xlog FROM regressuser1;
 
 -- clean up
 
-- 
2.5.0

#124Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#121)
Re: Additional role attributes && superuser review

Stephen Frost wrote:

Even so, in the interest of having more fine-grained permission
controls, I've gone ahead and added a pg_switch_xlog default role.
Note that this means that pg_switch_xlog() can be called by both
pg_switch_xlog roles and pg_backup roles. I'd be very much against
removing the ability to call pg_switch_xlog from the pg_backup role as
that really is a capability which is needed by users running backups and
it'd just add unnecessary complexity to require users setting up backup
tools to grant two different roles to get the backup to work.

Isn't it simpler to grant pg_switch_xlog to pg_backup in the default
config?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#125Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#124)
Re: Additional role attributes && superuser review

On Tuesday, November 24, 2015, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

Stephen Frost wrote:

Even so, in the interest of having more fine-grained permission
controls, I've gone ahead and added a pg_switch_xlog default role.
Note that this means that pg_switch_xlog() can be called by both
pg_switch_xlog roles and pg_backup roles. I'd be very much against
removing the ability to call pg_switch_xlog from the pg_backup role as
that really is a capability which is needed by users running backups and
it'd just add unnecessary complexity to require users setting up backup
tools to grant two different roles to get the backup to work.

Isn't it simpler to grant pg_switch_xlog to pg_backup in the default
config?

I'm not against it, but it would imply a set of data lines for
pg_auth_members, which we don't have today. We can't easily directly GRANT
the role due to the restrictions put in place to prevent regular users from
changing the system roles. On the other hand, we could change the check to
only apply when we aren't in bootstrap mode.

Thanks!

Stephen

#126Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#121)
Re: Additional role attributes && superuser review

On Fri, Nov 20, 2015 at 12:29 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

As David notes, they're actually quite related. Note that in our
documentation pg_switch_xlog() is listed in the "Backup Control
Functions" table.

I can think of a use-case for a user who can call pg_switch_xlog, but
not pg_start_backup()/pg_stop_backup(), but I have to admit that it
seems rather limited and I'm on the fence about it being a worthwhile
distinction.

Sounds too narrow to me. Are we going to have a separate predefined
role for every security-restricted function to which someone might
want to grant access? That seems over the top to me.

I don't think we should make it our goal to completely eliminate the
use of SECURITY DEFINER functions for privilege delegation. Of
course, being able to grant privileges directly is nicer, because then
the client code doesn't have to know about it. But I think it's OK,
even good, if the predefined roles cater to the common cases, and the
less common cases aren't handled quite as elegantly.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#127Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#126)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Fri, Nov 20, 2015 at 12:29 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

As David notes, they're actually quite related. Note that in our
documentation pg_switch_xlog() is listed in the "Backup Control
Functions" table.

I can think of a use-case for a user who can call pg_switch_xlog, but
not pg_start_backup()/pg_stop_backup(), but I have to admit that it
seems rather limited and I'm on the fence about it being a worthwhile
distinction.

Sounds too narrow to me. Are we going to have a separate predefined
role for every security-restricted function to which someone might
want to grant access? That seems over the top to me.

I certainly don't want to go down to that level and was, as seen above,
unsure about having pg_switch_xlog() as a differentiated privilege.
Michael, do you still see that as a useful independent capability?

I don't think we should make it our goal to completely eliminate the
use of SECURITY DEFINER functions for privilege delegation. Of
course, being able to grant privileges directly is nicer, because then
the client code doesn't have to know about it. But I think it's OK,
even good, if the predefined roles cater to the common cases, and the
less common cases aren't handled quite as elegantly.

Agreed.

Thanks!

Stephen

#128Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#127)
Re: Additional role attributes && superuser review

Stephen Frost wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

I can think of a use-case for a user who can call pg_switch_xlog, but
not pg_start_backup()/pg_stop_backup(), but I have to admit that it
seems rather limited and I'm on the fence about it being a worthwhile
distinction.

Sounds too narrow to me. Are we going to have a separate predefined
role for every security-restricted function to which someone might
want to grant access? That seems over the top to me.

I certainly don't want to go down to that level and was, as seen above,
unsure about having pg_switch_xlog() as a differentiated privilege.
Michael, do you still see that as a useful independent capability?

Hmm, Robert's argument seems reasonable -- we can continue to offer
access to individual elements by granting execute on a security-definer
function owned by predefined user pg_backup.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#129Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#127)
Re: Additional role attributes && superuser review

On Tue, Dec 1, 2015 at 3:32 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Fri, Nov 20, 2015 at 12:29 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

As David notes, they're actually quite related. Note that in our
documentation pg_switch_xlog() is listed in the "Backup Control
Functions" table.

I can think of a use-case for a user who can call pg_switch_xlog, but
not pg_start_backup()/pg_stop_backup(), but I have to admit that it
seems rather limited and I'm on the fence about it being a worthwhile
distinction.

Sounds too narrow to me. Are we going to have a separate predefined
role for every security-restricted function to which someone might
want to grant access? That seems over the top to me.

I certainly don't want to go down to that level and was, as seen above,
unsure about having pg_switch_xlog() as a differentiated privilege.
Michael, do you still see that as a useful independent capability?

OK, let's do so then by having this one fall under pg_backup. Let's
not be my grunting concerns be an obstacle for this patch, and we
could still change it afterwards in this release beta cycle anyway
based on user feedback.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#130Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#129)
Re: Additional role attributes && superuser review

On Tue, Dec 1, 2015 at 9:18 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Dec 1, 2015 at 3:32 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Fri, Nov 20, 2015 at 12:29 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Thu, Nov 19, 2015 at 7:10 AM, Stephen Frost wrote:

* Michael Paquier (michael.paquier@gmail.com) wrote:

It seems weird to not have a dedicated role for pg_switch_xlog.

I didn't add a pg_switch_xlog default role in this patch series, but
would be happy to do so if that's the consensus. It's quite easy to do.

Agreed. I am not actually getting why that's part of the backup
actually. That would be more related to archiving, both being
unrelated concepts. But at this point I guess that's mainly a
philosophical split.

As David notes, they're actually quite related. Note that in our
documentation pg_switch_xlog() is listed in the "Backup Control
Functions" table.

I can think of a use-case for a user who can call pg_switch_xlog, but
not pg_start_backup()/pg_stop_backup(), but I have to admit that it
seems rather limited and I'm on the fence about it being a worthwhile
distinction.

Sounds too narrow to me. Are we going to have a separate predefined
role for every security-restricted function to which someone might
want to grant access? That seems over the top to me.

I certainly don't want to go down to that level and was, as seen above,
unsure about having pg_switch_xlog() as a differentiated privilege.
Michael, do you still see that as a useful independent capability?

OK, let's do so then by having this one fall under pg_backup. Let's
not be my grunting concerns be an obstacle for this patch, and we
could still change it afterwards in this release beta cycle anyway
based on user feedback.

Three weeks later...
This thread has not moved a iota. Stephen, are you planning to work
more on this patch? It seems that we found a consensus. If nothing
happens, I am afraid that the destiny of this patch will be to be
returned with feedback, it is the 5th CF where this entry is
registered.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#131Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#130)
Re: Additional role attributes && superuser review

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Tue, Dec 1, 2015 at 9:18 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

OK, let's do so then by having this one fall under pg_backup. Let's
not be my grunting concerns be an obstacle for this patch, and we
could still change it afterwards in this release beta cycle anyway
based on user feedback.

Three weeks later...
This thread has not moved a iota. Stephen, are you planning to work
more on this patch? It seems that we found a consensus. If nothing
happens, I am afraid that the destiny of this patch will be to be
returned with feedback, it is the 5th CF where this entry is
registered.

Ok, seems you're right that we've got consensus on it. I'll post an
updated patch later today which I'll plan to commit.

Thanks!

Stephen

#132Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#129)
1 attachment(s)
Re: Additional role attributes && superuser review

Michael, all,

* Michael Paquier (michael.paquier@gmail.com) wrote:

OK, let's do so then by having this one fall under pg_backup. Let's
not be my grunting concerns be an obstacle for this patch, and we
could still change it afterwards in this release beta cycle anyway
based on user feedback.

Updated and rebased patch attached which takes the 'pg_switch_xlog'
default role back out, leaving us with:

pg_monitor - View privileged info
pg_backup - start/stop backups, switch xlog, create restore points
pg_replay - Pause/resume xlog replay on replicas
pg_replication - Create/destroy/etc replication slots
pg_rotate_logfile - Request logfile rotation
pg_signal_backend - Signal other backends (cancel query/terminate)
pg_file_settings - View configuration settings in all config files

Michael, another review would be great, if you don't mind. I'm going to
be going through it also more closely since it sounds like we've got
consensus on at least this initial set of default roles and rights. If
all looks good then I'll commit it.

Thanks!

Stephen

Attachments:

default_roles_v10.patchtext/x-diff; charset=us-asciiDownload
From 59bda4266a96976547e0aa44874ad716bf3dbdc9 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 18 Nov 2015 11:50:57 -0500
Subject: [PATCH 1/3] Add note regarding permissions in pg_catalog

Add a note to the system catalog section pointing out that while
modifying the permissions on catalog tables is possible, it's
unlikely to have the desired effect.
---
 doc/src/sgml/catalogs.sgml | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 97ef618..3b7768c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,17 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <note>
+   <para>
+    Changing the permissions on objects in the system catalogs, while
+    possible, is unlikely to have the desired effect as the internal
+    lookup functions use a cache and do not check the permissions nor
+    policies of tables in the system catalog.  Further, permission
+    changes to objects in the system catalogs are not preserved by
+    pg_dump or across upgrades.
+   </para>
+  </note>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
-- 
2.5.0


From a659b7ecd220c0671d3cc272b3774318bce97567 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:04:55 -0400
Subject: [PATCH 2/3] Reserve the "pg_" namespace for roles

This will prevent users from creating roles which begin with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.
---
 doc/src/sgml/ref/psql-ref.sgml          |  8 +++++--
 src/backend/catalog/catalog.c           |  5 ++--
 src/backend/commands/user.c             | 40 ++++++++++++++++++++++++++++++++
 src/backend/utils/adt/acl.c             | 41 +++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dumpall.c            |  2 ++
 src/bin/pg_upgrade/check.c              | 40 ++++++++++++++++++++++++++++++--
 src/bin/psql/command.c                  |  4 ++--
 src/bin/psql/describe.c                 |  5 +++-
 src/bin/psql/describe.h                 |  2 +-
 src/bin/psql/help.c                     |  4 ++--
 src/include/utils/acl.h                 |  1 +
 src/test/regress/expected/rolenames.out | 18 +++++++++++++++
 src/test/regress/sql/rolenames.sql      |  8 +++++++
 13 files changed, 166 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 6d0cb3d..76bb642 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1365,13 +1365,15 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\dg[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dg[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\du</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\dg+</literal> is used, additional information
@@ -1525,13 +1527,15 @@ testdb=&gt;
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\du[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\dg</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\du+</literal> is used, additional information
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 2961ccb..03382f2 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role in the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
@@ -1224,10 +1256,18 @@ GrantRole(GrantRoleStmt *stmt)
 	ListCell   *item;
 
 	if (stmt->grantor)
+	{
+		check_rolespec_name(stmt->grantor,
+							"Cannot specify reserved role as grantor.");
 		grantor = get_rolespec_oid(stmt->grantor, false);
+	}
 	else
 		grantor = GetUserId();
 
+	foreach(item, stmt->grantee_roles)
+		check_rolespec_name(lfirst(item),
+							"Cannot GRANT roles to a reserved role.");
+
 	grantee_ids = roleSpecsToIds(stmt->grantee_roles);
 
 	/* AccessShareLock is enough since we aren't modifying pg_authid */
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3ca168b..eb153a3 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
@@ -5247,3 +5248,43 @@ get_rolespec_name(const Node *node)
 
 	return rolename;
 }
+
+/*
+ * Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
+ * if provided.
+ *
+ * If node is NULL, no error is thrown.  If detail_msg is NULL then no detail
+ * message is provided.
+ */
+void
+check_rolespec_name(const Node *node, const char *detail_msg)
+{
+	RoleSpec   *role;
+
+	if (!node)
+		return;
+
+	role = (RoleSpec *) node;
+	if (!IsA(node, RoleSpec))
+		elog(ERROR, "invalid node type %d", node->type);
+
+	if (role->roletype != ROLESPEC_CSTRING)
+		return;
+
+	if (IsReservedName(role->rolename))
+	{
+		if (detail_msg)
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename),
+					 errdetail("%s", detail_msg)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename)));
+	}
+
+	return;
+}
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 539f197..70b33eb 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index cf6876b..31c7af7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -429,7 +429,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -474,7 +474,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index bb59bc2..32cd58b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2646,7 +2646,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2691,6 +2691,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5f240be..70223cc 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 915ea39..5fccd88 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -231,6 +231,7 @@ extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid	get_role_oid(const char *rolename, bool missing_ok);
 extern Oid	get_role_oid_or_public(const char *rolename);
 extern Oid	get_rolespec_oid(const Node *node, bool missing_ok);
+extern void	check_rolespec_name(const Node *node, const char *detail_msg);
 extern HeapTuple get_rolespec_tuple(const Node *node);
 extern char *get_rolespec_name(const Node *node);
 
diff --git a/src/test/regress/expected/rolenames.out b/src/test/regress/expected/rolenames.out
index 8f88c02..c9be282 100644
--- a/src/test/regress/expected/rolenames.out
+++ b/src/test/regress/expected/rolenames.out
@@ -78,6 +78,18 @@ CREATE ROLE "none"; -- error
 ERROR:  role name "none" is reserved
 LINE 1: CREATE ROLE "none";
                     ^
+CREATE ROLE pg_abc; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abc"; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE pg_backup; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_backup"; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -804,6 +816,12 @@ LINE 1: DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9;
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 NOTICE:  role "nonexistent" does not exist, skipping
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
  proname  | proacl 
diff --git a/src/test/regress/sql/rolenames.sql b/src/test/regress/sql/rolenames.sql
index e8c6b33..65c97ec 100644
--- a/src/test/regress/sql/rolenames.sql
+++ b/src/test/regress/sql/rolenames.sql
@@ -57,6 +57,11 @@ CREATE ROLE "public"; -- error
 CREATE ROLE none; -- error
 CREATE ROLE "none"; -- error
 
+CREATE ROLE pg_abc; -- error
+CREATE ROLE "pg_abc"; -- error
+CREATE ROLE pg_backup; -- error
+CREATE ROLE "pg_backup"; -- error
+
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -376,6 +381,9 @@ DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+GRANT pg_backup TO pg_monitor; -- error
+
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
 
-- 
2.5.0


From 5ad92938a3c9941a912e7cbc18b80f8637078a20 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:08:03 -0400
Subject: [PATCH 3/3] Create default roles

This creates an initial set of default roles which administrators may
use to grant access to, historically, superuser-only functions.  Using
these roles instead of granting superuser access reduces the number of
superuser roles required for a system.  Documention for each of the
default roles has been added to user-manag.sgml.
---
 contrib/test_decoding/expected/permissions.out |   8 +-
 doc/src/sgml/func.sgml                         |  23 ++--
 doc/src/sgml/user-manag.sgml                   |  83 +++++++++++++++
 src/backend/access/transam/xlogfuncs.c         |  54 +++++++---
 src/backend/catalog/system_views.sql           |   2 +
 src/backend/replication/logical/logicalfuncs.c |  17 ++-
 src/backend/replication/slotfuncs.c            |  29 ++---
 src/backend/replication/walsender.c            |   8 +-
 src/backend/utils/adt/misc.c                   |  12 ++-
 src/backend/utils/adt/pgstatfuncs.c            |  25 +++--
 src/backend/utils/misc/guc.c                   |   7 ++
 src/include/catalog/pg_authid.h                |  20 +++-
 src/test/regress/expected/privileges.out       | 142 +++++++++++++++++++++++++
 src/test/regress/sql/privileges.sql            |  83 +++++++++++++++
 14 files changed, 448 insertions(+), 65 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 60b9a09..5c110cc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16678,7 +16678,8 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Cancel a backend's current query.  This is also allowed if the
-        calling role is a member of the role whose backend is being canceled,
+        calling role is a member of the role whose backend is being canceled or
+        the calling role has been granted <literal>pg_signal_backend</literal>,
         however only superusers can cancel superuser backends.
         </entry>
       </row>
@@ -16702,8 +16703,9 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Terminate a backend.  This is also allowed if the calling role
-        is a member of the role whose backend is being terminated, however only
-        superusers can terminate superuser backends.
+        is a member of the role whose backend is being terminated or the
+        calling role has been granted <literal>pg_signal_backend</literal>,
+        however only superusers can terminate superuser backends.
        </entry>
       </row>
      </tbody>
@@ -16807,7 +16809,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Create a named point for performing restore (restricted to superusers)</entry>
+       <entry>Create a named point for performing restore (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16828,14 +16830,14 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Prepare for performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Finish performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16856,7 +16858,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
+       <entry>Force switch to a new transaction log file (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -17112,7 +17114,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_pause()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Pauses recovery immediately (restricted to superusers).
+       <entry>Pauses recovery immediately (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
       <row>
@@ -17120,7 +17122,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_resume()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Restarts recovery if it was paused (restricted to superusers).
+       <entry>Restarts recovery if it was paused (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
      </tbody>
@@ -17230,7 +17232,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
     See <xref linkend="streaming-replication">,
     <xref linkend="streaming-replication-slots">, <xref linkend="replication-origins">
     for information about the underlying features.  Use of these
-    functions is restricted to superusers.
+    functions is restricted to superuser or <literal>pg_replication</literal>
+    roles.
    </para>
 
    <para>
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index d1b6e59..9975c77 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -483,6 +483,89 @@ DROP ROLE doomed_role;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch to a new transaction log file, view transaction log position information, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>View configuration settings from all configuration files.</entry>
+      </row>
+      <row>
+       <entry>pg_monitor</entry>
+       <entry>View privileged system information (eg: activity of other users, transaction log position information).</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume transaction log replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Request logfile rotation.</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate).</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..1c5f56c 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +145,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -183,6 +189,12 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view current xlog location")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -204,6 +216,12 @@ pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view current xlog insert location")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -226,6 +244,12 @@ pg_last_xlog_receive_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view last xlog receive location")));
+
 	recptr = GetWalRcvWriteRecPtr(NULL, NULL);
 
 	if (recptr == 0)
@@ -245,6 +269,12 @@ pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view last xlog replay location")));
+
 	recptr = GetXLogReplayRecPtr(NULL);
 
 	if (recptr == 0)
@@ -348,10 +378,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +400,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 536c805..c00d5f4 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -426,6 +426,8 @@ CREATE VIEW pg_file_settings AS
 
 REVOKE ALL on pg_file_settings FROM PUBLIC;
 REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC;
+GRANT SELECT on pg_file_settings TO pg_file_settings;
+GRANT EXECUTE ON FUNCTION pg_show_all_file_settings() TO pg_file_settings;
 
 CREATE VIEW pg_timezone_abbrevs AS
     SELECT * FROM pg_timezone_abbrevs();
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 012987a..968ad93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 7b1b7f1..fc82201 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2811,11 +2813,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index 3ef6e43..c320966 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being canceled"))));
+				 (errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..4946903 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8249,6 +8251,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all configuration file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index 2c8565e..9f79b99 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -93,10 +93,28 @@ typedef FormData_pg_authid *Form_pg_authid;
  *
  * The uppercase quantities will be replaced at initdb time with
  * user choices.
+ *
+ * If adding new default roles or changing the OIDs below, be sure to add or
+ * update the #defines which follow as appropriate.
  * ----------------
  */
 DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 4200 ( "pg_monitor" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4201 ( "pg_backup" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4202 ( "pg_replay" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4203 ( "pg_replication" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4204 ( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4205 ( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4206 ( "pg_file_settings" f t f f f f f -1 _null_ _null_));
+
+#define BOOTSTRAP_SUPERUSERID			10
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define DEFAULT_ROLE_MONITORID			4200
+#define DEFAULT_ROLE_BACKUPID			4201
+#define DEFAULT_ROLE_REPLAYID			4202
+#define DEFAULT_ROLE_REPLICATIONID		4203
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	4204
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	4205
+#define DEFAULT_ROLE_FILE_SETTINGSID	4206
 
 #endif   /* PG_AUTHID_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 88bdc2c..aeac29a 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1523,6 +1523,148 @@ revoke select on dep_priv_test from regressuser4 cascade;
 
 set session role regressuser1;
 drop table dep_priv_test;
+-- test default roles
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup to switch transaction log files
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog replay location
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_backup FROM regressuser1;
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for function pg_show_all_file_settings
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for relation pg_file_settings
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+\c
+REVOKE pg_file_settings FROM regressuser1;
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog replay location
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_monitor FROM regressuser1;
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+SELECT pg_xlog_replay_resume(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+\c
+REVOKE pg_replay FROM regressuser1;
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
 -- clean up
 \c
 drop sequence x_seq;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c1837c4..a2815e2 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -932,6 +932,89 @@ revoke select on dep_priv_test from regressuser4 cascade;
 set session role regressuser1;
 drop table dep_priv_test;
 
+-- test default roles
+
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT pg_switch_xlog(); -- fail-no-perm
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_backup FROM regressuser1;
+
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+\c
+REVOKE pg_file_settings FROM regressuser1;
+
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_monitor FROM regressuser1;
+
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+SELECT pg_xlog_replay_resume(); -- fail
+\c
+REVOKE pg_replay FROM regressuser1;
+
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
 
 -- clean up
 
-- 
2.5.0

#133Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#132)
Re: Additional role attributes && superuser review

On Tue, Dec 22, 2015 at 1:41 AM, Stephen Frost <sfrost@snowman.net> wrote:

Updated and rebased patch attached which takes the 'pg_switch_xlog'
default role back out, leaving us with:

pg_monitor - View privileged info
pg_backup - start/stop backups, switch xlog, create restore points
pg_replay - Pause/resume xlog replay on replicas
pg_replication - Create/destroy/etc replication slots
pg_rotate_logfile - Request logfile rotation
pg_signal_backend - Signal other backends (cancel query/terminate)
pg_file_settings - View configuration settings in all config files

Michael, another review would be great, if you don't mind. I'm going to
be going through it also more closely since it sounds like we've got
consensus on at least this initial set of default roles and rights. If
all looks good then I'll commit it.

Thanks. This looks fine to me. I just have one single comment:

+       <entry>Request logfile rotation.</entry>
s/logfile/transaction log file/
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#134Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Michael Paquier (#133)
Re: Additional role attributes && superuser review

On 2015/12/22 14:05, Michael Paquier wrote:

On Tue, Dec 22, 2015 at 1:41 AM, Stephen Frost <sfrost@snowman.net> wrote:

Updated and rebased patch attached which takes the 'pg_switch_xlog'
default role back out, leaving us with:

pg_monitor - View privileged info
pg_backup - start/stop backups, switch xlog, create restore points
pg_replay - Pause/resume xlog replay on replicas
pg_replication - Create/destroy/etc replication slots
pg_rotate_logfile - Request logfile rotation
pg_signal_backend - Signal other backends (cancel query/terminate)
pg_file_settings - View configuration settings in all config files

Thanks. This looks fine to me. I just have one single comment:

+ <entry>Request logfile rotation.</entry>
s/logfile/transaction log file/

Looks correct as is. Or maybe "server's log file" as in:

9.26.2. Server Signaling Functions

pg_rotate_logfile(): Rotate server's log file

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#135Michael Paquier
michael.paquier@gmail.com
In reply to: Amit Langote (#134)
Re: Additional role attributes && superuser review

On Tue, Dec 22, 2015 at 2:54 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

On 2015/12/22 14:05, Michael Paquier wrote:

On Tue, Dec 22, 2015 at 1:41 AM, Stephen Frost <sfrost@snowman.net> wrote:

Updated and rebased patch attached which takes the 'pg_switch_xlog'
default role back out, leaving us with:

pg_monitor - View privileged info
pg_backup - start/stop backups, switch xlog, create restore points
pg_replay - Pause/resume xlog replay on replicas
pg_replication - Create/destroy/etc replication slots
pg_rotate_logfile - Request logfile rotation
pg_signal_backend - Signal other backends (cancel query/terminate)
pg_file_settings - View configuration settings in all config files

Thanks. This looks fine to me. I just have one single comment:

+ <entry>Request logfile rotation.</entry>
s/logfile/transaction log file/

Looks correct as is. Or maybe "server's log file" as in:

9.26.2. Server Signaling Functions

pg_rotate_logfile(): Rotate server's log file

You're right, this is not a WAL segment, just a normal log file. Your
phrasing is better.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#136Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#135)
1 attachment(s)
Re: Additional role attributes && superuser review

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Tue, Dec 22, 2015 at 2:54 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

On 2015/12/22 14:05, Michael Paquier wrote:

On Tue, Dec 22, 2015 at 1:41 AM, Stephen Frost <sfrost@snowman.net> wrote:

Updated and rebased patch attached which takes the 'pg_switch_xlog'
default role back out, leaving us with:

pg_monitor - View privileged info
pg_backup - start/stop backups, switch xlog, create restore points
pg_replay - Pause/resume xlog replay on replicas
pg_replication - Create/destroy/etc replication slots
pg_rotate_logfile - Request logfile rotation
pg_signal_backend - Signal other backends (cancel query/terminate)
pg_file_settings - View configuration settings in all config files

Thanks. This looks fine to me. I just have one single comment:

+ <entry>Request logfile rotation.</entry>
s/logfile/transaction log file/

Looks correct as is. Or maybe "server's log file" as in:

9.26.2. Server Signaling Functions

pg_rotate_logfile(): Rotate server's log file

You're right, this is not a WAL segment, just a normal log file. Your
phrasing is better.

Works for me.

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

Thanks!

Stephen

Attachments:

default_roles_v11.patchtext/x-diff; charset=us-asciiDownload
From f493051be96514c0f0e178ef74d6d824f702a7c2 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 18 Nov 2015 11:50:57 -0500
Subject: [PATCH 1/3] Add note regarding permissions in pg_catalog

Add a note to the system catalog section pointing out that while
modifying the permissions on catalog tables is possible, it's
unlikely to have the desired effect.
---
 doc/src/sgml/catalogs.sgml | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 97ef618..3b7768c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -21,6 +21,17 @@
    particularly esoteric operations, such as adding index access methods.
   </para>
 
+  <note>
+   <para>
+    Changing the permissions on objects in the system catalogs, while
+    possible, is unlikely to have the desired effect as the internal
+    lookup functions use a cache and do not check the permissions nor
+    policies of tables in the system catalog.  Further, permission
+    changes to objects in the system catalogs are not preserved by
+    pg_dump or across upgrades.
+   </para>
+  </note>
+
  <sect1 id="catalogs-overview">
   <title>Overview</title>
 
-- 
2.5.0


From dece838e3ad74549c6f07dc878c8637dc2db674d Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:04:55 -0400
Subject: [PATCH 2/3] Reserve the "pg_" namespace for roles

This will prevent users from creating roles which begin with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.
---
 doc/src/sgml/ref/psql-ref.sgml          |  8 +++++--
 src/backend/catalog/catalog.c           |  5 ++--
 src/backend/commands/user.c             | 40 ++++++++++++++++++++++++++++++++
 src/backend/utils/adt/acl.c             | 41 +++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dumpall.c            |  2 ++
 src/bin/pg_upgrade/check.c              | 40 ++++++++++++++++++++++++++++++--
 src/bin/psql/command.c                  |  4 ++--
 src/bin/psql/describe.c                 |  5 +++-
 src/bin/psql/describe.h                 |  2 +-
 src/bin/psql/help.c                     |  4 ++--
 src/include/utils/acl.h                 |  1 +
 src/test/regress/expected/rolenames.out | 18 +++++++++++++++
 src/test/regress/sql/rolenames.sql      |  8 +++++++
 13 files changed, 166 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 6d0cb3d..76bb642 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1365,13 +1365,15 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\dg[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dg[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\du</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\dg+</literal> is used, additional information
@@ -1525,13 +1527,15 @@ testdb=&gt;
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\du[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\dg</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\du+</literal> is used, additional information
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 81ccebf..184aa7d 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *		True iff name starts with the pg_ prefix.
  *
  *		For some classes of objects, the prefix pg_ is reserved for
- *		system objects only.  As of 8.0, this is only true for
- *		schema and tablespace names.
+ *		system objects only.  As of 8.0, this was only true for
+ *		schema and tablespace names.  With 9.6, this is also true
+ *		for roles.
  */
 bool
 IsReservedName(const char *name)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 2961ccb..03382f2 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -312,6 +313,17 @@ CreateRole(CreateRoleStmt *stmt)
 	}
 
 	/*
+	 * Check that the user is not trying to create a role in the reserved
+	 * "pg_" namespace.
+	 */
+	if (IsReservedName(stmt->role))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 stmt->role),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	/*
 	 * Check the pg_authid relation to be certain the role doesn't already
 	 * exist.
 	 */
@@ -1117,6 +1129,7 @@ RenameRole(const char *oldname, const char *newname)
 	int			i;
 	Oid			roleid;
 	ObjectAddress address;
+	Form_pg_authid authform;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1136,6 +1149,7 @@ RenameRole(const char *oldname, const char *newname)
 	 */
 
 	roleid = HeapTupleGetOid(oldtuple);
+	authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
 	if (roleid == GetSessionUserId())
 		ereport(ERROR,
@@ -1146,6 +1160,24 @@ RenameRole(const char *oldname, const char *newname)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("current user cannot be renamed")));
 
+	/*
+	 * Check that the user is not trying to rename a system role and
+	 * not trying to rename a role into the reserved "pg_" namespace.
+	 */
+	if (IsReservedName(NameStr(authform->rolname)))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 NameStr(authform->rolname)),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
+	if (IsReservedName(newname))
+		ereport(ERROR,
+				(errcode(ERRCODE_RESERVED_NAME),
+				 errmsg("role name \"%s\" is reserved",
+					 newname),
+				 errdetail("Role names starting with \"pg_\" are reserved.")));
+
 	/* make sure the new name doesn't exist */
 	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
 		ereport(ERROR,
@@ -1224,10 +1256,18 @@ GrantRole(GrantRoleStmt *stmt)
 	ListCell   *item;
 
 	if (stmt->grantor)
+	{
+		check_rolespec_name(stmt->grantor,
+							"Cannot specify reserved role as grantor.");
 		grantor = get_rolespec_oid(stmt->grantor, false);
+	}
 	else
 		grantor = GetUserId();
 
+	foreach(item, stmt->grantee_roles)
+		check_rolespec_name(lfirst(item),
+							"Cannot GRANT roles to a reserved role.");
+
 	grantee_ids = roleSpecsToIds(stmt->grantee_roles);
 
 	/* AccessShareLock is enough since we aren't modifying pg_authid */
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3ca168b..eb153a3 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
@@ -5247,3 +5248,43 @@ get_rolespec_name(const Node *node)
 
 	return rolename;
 }
+
+/*
+ * Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
+ * if provided.
+ *
+ * If node is NULL, no error is thrown.  If detail_msg is NULL then no detail
+ * message is provided.
+ */
+void
+check_rolespec_name(const Node *node, const char *detail_msg)
+{
+	RoleSpec   *role;
+
+	if (!node)
+		return;
+
+	role = (RoleSpec *) node;
+	if (!IsA(node, RoleSpec))
+		elog(ERROR, "invalid node type %d", node->type);
+
+	if (role->roletype != ROLESPEC_CSTRING)
+		return;
+
+	if (IsReservedName(role->rolename))
+	{
+		if (detail_msg)
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename),
+					 errdetail("%s", detail_msg)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_RESERVED_NAME),
+					 errmsg("role \"%s\" is reserved",
+						 role->rolename)));
+	}
+
+	return;
+}
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 3461335..addabd0 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -673,6 +673,7 @@ dumpRoles(PGconn *conn)
 			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
 						  "rolname = current_user AS is_current_user "
 						  "FROM pg_authid "
+						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2");
 	else if (server_version >= 90100)
 		printfPQExpBuffer(buf,
@@ -895,6 +896,7 @@ dumpRoleMembership(PGconn *conn)
 					   "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
 					   "LEFT JOIN pg_authid um on um.oid = a.member "
 					   "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+					   "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
 					   "ORDER BY 1,2,3");
 
 	if (PQntuples(res) > 0)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 539f197..70b33eb 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -98,6 +99,11 @@ check_and_dump_old_cluster(bool live_check)
 	check_for_prepared_transactions(&old_cluster);
 	check_for_reg_data_type_usage(&old_cluster);
 	check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+	/* 9.5 and below should not have roles starting with pg_ */
+	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+		check_for_pg_role_prefix(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -613,7 +619,8 @@ check_is_install_user(ClusterInfo *cluster)
 	res = executeQueryOrDie(conn,
 							"SELECT rolsuper, oid "
 							"FROM pg_catalog.pg_roles "
-							"WHERE rolname = current_user");
+							"WHERE rolname = current_user "
+							"AND rolname !~ '^pg_'");
 
 	/*
 	 * We only allow the install user in the new cluster (see comment below)
@@ -629,7 +636,8 @@ check_is_install_user(ClusterInfo *cluster)
 
 	res = executeQueryOrDie(conn,
 							"SELECT COUNT(*) "
-							"FROM pg_catalog.pg_roles ");
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname !~ '^pg_'");
 
 	if (PQntuples(res) != 1)
 		pg_fatal("could not determine the number of users\n");
@@ -1017,6 +1025,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *	Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+	PGresult   *res;
+	PGconn	   *conn = connectToServer(cluster, "template1");
+
+	prep_status("Checking for roles starting with 'pg_'");
+
+	res = executeQueryOrDie(conn,
+							"SELECT * "
+							"FROM pg_catalog.pg_roles "
+							"WHERE rolname ~ '^pg_'");
+
+	if (PQntuples(res) != 0)
+		pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+				 CLUSTER_NAME(cluster));
+
+	PQclear(res);
+
+	PQfinish(conn);
+
+	check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index cf6876b..31c7af7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -429,7 +429,7 @@ exec_command(const char *cmd,
 				break;
 			case 'g':
 				/* no longer distinct from \du */
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'l':
 				success = do_lo_list();
@@ -474,7 +474,7 @@ exec_command(const char *cmd,
 					success = PSQL_CMD_UNKNOWN;
 				break;
 			case 'u':
-				success = describeRoles(pattern, show_verbose);
+				success = describeRoles(pattern, show_verbose, show_system);
 				break;
 			case 'F':			/* text search subsystem */
 				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index bb59bc2..32cd58b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2646,7 +2646,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -2691,6 +2691,9 @@ describeRoles(const char *pattern, bool verbose)
 
 		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+		if (!showSystem && !pattern)
+			appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
 		processSQLNamePattern(pset.db, &buf, pattern, false, false,
 							  NULL, "r.rolname", NULL, NULL);
 	}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 822e71a..9e31c02 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5f240be..70223cc 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -227,7 +227,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
 	fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
 	fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-	fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
 	fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
 	fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -240,7 +240,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
 	fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
 	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-	fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+	fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
 	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
 	fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
 	fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 915ea39..5fccd88 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -231,6 +231,7 @@ extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid	get_role_oid(const char *rolename, bool missing_ok);
 extern Oid	get_role_oid_or_public(const char *rolename);
 extern Oid	get_rolespec_oid(const Node *node, bool missing_ok);
+extern void	check_rolespec_name(const Node *node, const char *detail_msg);
 extern HeapTuple get_rolespec_tuple(const Node *node);
 extern char *get_rolespec_name(const Node *node);
 
diff --git a/src/test/regress/expected/rolenames.out b/src/test/regress/expected/rolenames.out
index 8f88c02..c9be282 100644
--- a/src/test/regress/expected/rolenames.out
+++ b/src/test/regress/expected/rolenames.out
@@ -78,6 +78,18 @@ CREATE ROLE "none"; -- error
 ERROR:  role name "none" is reserved
 LINE 1: CREATE ROLE "none";
                     ^
+CREATE ROLE pg_abc; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abc"; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE pg_backup; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_backup"; -- error
+ERROR:  role name "pg_backup" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -804,6 +816,12 @@ LINE 1: DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9;
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 NOTICE:  role "nonexistent" does not exist, skipping
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+ERROR:  role "pg_backup" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
+GRANT pg_backup TO pg_monitor; -- error
+ERROR:  role "pg_monitor" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
  proname  | proacl 
diff --git a/src/test/regress/sql/rolenames.sql b/src/test/regress/sql/rolenames.sql
index e8c6b33..65c97ec 100644
--- a/src/test/regress/sql/rolenames.sql
+++ b/src/test/regress/sql/rolenames.sql
@@ -57,6 +57,11 @@ CREATE ROLE "public"; -- error
 CREATE ROLE none; -- error
 CREATE ROLE "none"; -- error
 
+CREATE ROLE pg_abc; -- error
+CREATE ROLE "pg_abc"; -- error
+CREATE ROLE pg_backup; -- error
+CREATE ROLE "pg_backup"; -- error
+
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -376,6 +381,9 @@ DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_backup; -- error
+GRANT pg_backup TO pg_monitor; -- error
+
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
 
-- 
2.5.0


From 297f18933d316fcf2ac09908cc8dd6fd5b100957 Mon Sep 17 00:00:00 2001
From: Stephen Frost <sfrost@snowman.net>
Date: Wed, 30 Sep 2015 07:08:03 -0400
Subject: [PATCH 3/3] Create default roles

This creates an initial set of default roles which administrators may
use to grant access to, historically, superuser-only functions.  Using
these roles instead of granting superuser access reduces the number of
superuser roles required for a system.  Documention for each of the
default roles has been added to user-manag.sgml.
---
 contrib/test_decoding/expected/permissions.out |   8 +-
 doc/src/sgml/func.sgml                         |  23 ++--
 doc/src/sgml/user-manag.sgml                   |  83 +++++++++++++++
 src/backend/access/transam/xlogfuncs.c         |  54 +++++++---
 src/backend/catalog/system_views.sql           |   2 +
 src/backend/replication/logical/logicalfuncs.c |  17 ++-
 src/backend/replication/slotfuncs.c            |  29 ++---
 src/backend/replication/walsender.c            |   8 +-
 src/backend/utils/adt/misc.c                   |  12 ++-
 src/backend/utils/adt/pgstatfuncs.c            |  25 +++--
 src/backend/utils/misc/guc.c                   |   7 ++
 src/include/catalog/pg_authid.h                |  20 +++-
 src/test/regress/expected/privileges.out       | 142 +++++++++++++++++++++++++
 src/test/regress/sql/privileges.sql            |  83 +++++++++++++++
 14 files changed, 448 insertions(+), 65 deletions(-)

diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out
index 212fd1d..79a7f86 100644
--- a/contrib/test_decoding/expected/permissions.out
+++ b/contrib/test_decoding/expected/permissions.out
@@ -54,13 +54,13 @@ RESET ROLE;
 -- plain user *can't* can control replication
 SET ROLE lr_normal;
 SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 INSERT INTO lr_test VALUES('lr_superuser_init');
 ERROR:  permission denied for relation lr_test
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- replication users can drop superuser created slots
 SET ROLE lr_superuser;
@@ -90,7 +90,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
 RESET ROLE;
 SET ROLE lr_normal;
 SELECT pg_drop_replication_slot('regression_slot');
-ERROR:  must be superuser or replication role to use replication slots
+ERROR:  must be superuser or member of pg_replication to use replication slots
 RESET ROLE;
 -- all users can see existing slots
 SET ROLE lr_superuser;
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 60b9a09..5c110cc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16678,7 +16678,8 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Cancel a backend's current query.  This is also allowed if the
-        calling role is a member of the role whose backend is being canceled,
+        calling role is a member of the role whose backend is being canceled or
+        the calling role has been granted <literal>pg_signal_backend</literal>,
         however only superusers can cancel superuser backends.
         </entry>
       </row>
@@ -16702,8 +16703,9 @@ SELECT set_config('log_statement_stats', 'off', false);
         </entry>
        <entry><type>boolean</type></entry>
        <entry>Terminate a backend.  This is also allowed if the calling role
-        is a member of the role whose backend is being terminated, however only
-        superusers can terminate superuser backends.
+        is a member of the role whose backend is being terminated or the
+        calling role has been granted <literal>pg_signal_backend</literal>,
+        however only superusers can terminate superuser backends.
        </entry>
       </row>
      </tbody>
@@ -16807,7 +16809,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Create a named point for performing restore (restricted to superusers)</entry>
+       <entry>Create a named point for performing restore (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16828,14 +16830,14 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Prepare for performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
         <literal><function>pg_stop_backup()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
+       <entry>Finish performing on-line backup (restricted to superusers, <literal>pg_backup</literal> or <literal>pg_replication</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -16856,7 +16858,7 @@ SELECT set_config('log_statement_stats', 'off', false);
         <literal><function>pg_switch_xlog()</function></literal>
         </entry>
        <entry><type>pg_lsn</type></entry>
-       <entry>Force switch to a new transaction log file (restricted to superusers)</entry>
+       <entry>Force switch to a new transaction log file (restricted to superusers or <literal>pg_backup</literal> roles)</entry>
       </row>
       <row>
        <entry>
@@ -17112,7 +17114,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_pause()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Pauses recovery immediately (restricted to superusers).
+       <entry>Pauses recovery immediately (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
       <row>
@@ -17120,7 +17122,7 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
         <literal><function>pg_xlog_replay_resume()</function></literal>
         </entry>
        <entry><type>void</type></entry>
-       <entry>Restarts recovery if it was paused (restricted to superusers).
+       <entry>Restarts recovery if it was paused (restricted to superusers or <literal>pg_replay</literal> roles).
        </entry>
       </row>
      </tbody>
@@ -17230,7 +17232,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
     See <xref linkend="streaming-replication">,
     <xref linkend="streaming-replication-slots">, <xref linkend="replication-origins">
     for information about the underlying features.  Use of these
-    functions is restricted to superusers.
+    functions is restricted to superuser or <literal>pg_replication</literal>
+    roles.
    </para>
 
    <para>
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index d1b6e59..f39250b 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -483,6 +483,89 @@ DROP ROLE doomed_role;
   </para>
  </sect1>
 
+ <sect1 id="default-roles">
+  <title>Default Roles</title>
+
+  <indexterm zone="default-roles">
+   <primary>role</>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> provides a set of default roles
+   which provide access to certain, commonly needed, privileged capabilities
+   and information.  Administrators can GRANT these roles to users and/or
+   other roles in their environment, providing those users with access to
+   the specified capabilities and information.
+  </para>
+
+  <para>
+   The default roles are described in <xref linkend="default-roles-table">.
+   Note that the specific permissions for each of the default roles may
+   change in the future as additional capabilities are added.  Administrators
+   should monitor the release notes for changes.
+  </para>
+
+   <table tocentry="1" id="default-roles-table">
+    <title>Default Roles</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Role</entry>
+       <entry>Allowed Access</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry>pg_backup</entry>
+       <entry>Start and stop backups, switch to a new transaction log file, view transaction log position information, and create restore points.</entry>
+      </row>
+      <row>
+       <entry>pg_file_settings</entry>
+       <entry>View configuration settings from all configuration files.</entry>
+      </row>
+      <row>
+       <entry>pg_monitor</entry>
+       <entry>View privileged system information (eg: activity of other users, transaction log position information).</entry>
+      </row>
+      <row>
+       <entry>pg_replay</entry>
+       <entry>Pause and resume transaction log replay on replicas.</entry>
+      </row>
+      <row>
+       <entry>pg_replication</entry>
+       <entry>Create, destroy, and work with replication slots.</entry>
+      </row>
+      <row>
+       <entry>pg_rotate_logfile</entry>
+       <entry>Rotate server's log file.</entry>
+      </row>
+      <row>
+       <entry>pg_signal_backend</entry>
+       <entry>Send signals to other backends (eg: cancel query, terminate).</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   Administrators can grant access to these roles to users using the GRANT
+   command:
+
+<programlisting>
+GRANT pg_backup TO backup_user;
+GRANT pg_monitor TO nagios;
+</programlisting>
+  </para>
+
+  <para>
+   Administrators should use the default roles for managing access to capabilities
+   and not change the permissions on the objects in the system catalogs, as such
+   changes are unlikely to have the desired effect and will not be preserved by
+   pg_dump or across upgrades.
+  </para>
+
+ </sect1>
+
  <sect1 id="perm-functions">
   <title>Function and Trigger Security</title>
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 329bb8c..1c5f56c 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,11 +22,13 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #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"
@@ -55,10 +57,12 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		   errmsg("must be superuser or replication role to run a backup")));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	/* Make sure we can open the directory with tablespaces in it */
 	dir = AllocateDir("pg_tblspc");
@@ -92,10 +96,12 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-		 (errmsg("must be superuser or replication role to run a backup"))));
+				 errmsg("must be superuser or member of pg_backup or pg_replication to run a backup")));
 
 	stoppoint = do_pg_stop_backup(NULL, true, NULL);
 
@@ -110,10 +116,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	switchpoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-			 (errmsg("must be superuser to switch transaction log files"))));
+				 errmsg("must be superuser or member of pg_backup to switch transaction log files")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -139,10 +145,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
 	char	   *restore_name_str;
 	XLogRecPtr	restorepoint;
 
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to create a restore point"))));
+				 errmsg("must be superuser or member of pg_backup to create a restore point")));
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
@@ -183,6 +189,12 @@ pg_current_xlog_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view current xlog location")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -204,6 +216,12 @@ pg_current_xlog_insert_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	current_recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view current xlog insert location")));
+
 	if (RecoveryInProgress())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -226,6 +244,12 @@ pg_last_xlog_receive_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view last xlog receive location")));
+
 	recptr = GetWalRcvWriteRecPtr(NULL, NULL);
 
 	if (recptr == 0)
@@ -245,6 +269,12 @@ pg_last_xlog_replay_location(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	recptr;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_BACKUPID) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_monitor or pg_backup to view last xlog replay location")));
+
 	recptr = GetXLogReplayRecPtr(NULL);
 
 	if (recptr == 0)
@@ -348,10 +378,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
@@ -370,10 +400,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS)
 Datum
 pg_xlog_replay_resume(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLAYID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to control recovery"))));
+				 errmsg("must be superuser or member of pg_replay to control recovery")));
 
 	if (!RecoveryInProgress())
 		ereport(ERROR,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 536c805..c00d5f4 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -426,6 +426,8 @@ CREATE VIEW pg_file_settings AS
 
 REVOKE ALL on pg_file_settings FROM PUBLIC;
 REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC;
+GRANT SELECT on pg_file_settings TO pg_file_settings;
+GRANT EXECUTE ON FUNCTION pg_show_all_file_settings() TO pg_file_settings;
 
 CREATE VIEW pg_timezone_abbrevs AS
     SELECT * FROM pg_timezone_abbrevs();
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 012987a..968ad93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -23,12 +23,14 @@
 
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 
 #include "nodes/makefuncs.h"
 
 #include "mb/pg_wchar.h"
 
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -202,15 +204,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 	}
 }
 
-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.
  *
@@ -324,7 +317,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b3c8140..421c6ed 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,21 +17,14 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #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"
 
-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.
@@ -52,7 +45,11 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
@@ -110,7 +107,11 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckLogicalDecodingRequirements();
 
@@ -159,7 +160,11 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 {
 	Name		name = PG_GETARG_NAME(0);
 
-	check_permissions();
+	if (!has_rolreplication(GetUserId()) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_REPLICATIONID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser or member of pg_replication to use replication slots"))));
 
 	CheckSlotRequirements();
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 7b1b7f1..fc82201 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -48,6 +48,7 @@
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
@@ -71,6 +72,7 @@
 #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"
@@ -2811,11 +2813,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
 		memset(nulls, 0, sizeof(nulls));
 		values[0] = Int32GetDatum(walsnd->pid);
 
-		if (!superuser())
+		if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			/*
-			 * Only superusers can see details. Other users only get the pid
-			 * value to know it's a walsender, but no details.
+			 * Only members of pg_monitor 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/misc.c b/src/backend/utils/adt/misc.c
index 3ef6e43..c320966 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_authid.h"
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -122,7 +123,8 @@ pg_signal_backend(int pid, int sig)
 		return SIGNAL_BACKEND_NOSUPERUSER;
 
 	/* Users can signal backends they have role membership in. */
-	if (!has_privs_of_role(GetUserId(), proc->roleId))
+	if (!has_privs_of_role(GetUserId(), proc->roleId) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
 		return SIGNAL_BACKEND_NOPERMISSION;
 
 	/*
@@ -168,7 +170,7 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose query is being canceled"))));
+				 (errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -192,7 +194,7 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 	if (r == SIGNAL_BACKEND_NOPERMISSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be a member of the role whose process is being terminated"))));
+				 (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
 
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
@@ -225,10 +227,10 @@ pg_reload_conf(PG_FUNCTION_ARGS)
 Datum
 pg_rotate_logfile(PG_FUNCTION_ARGS)
 {
-	if (!superuser())
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_ROTATE_LOGFILEID))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 (errmsg("must be superuser to rotate log files"))));
+				 (errmsg("must be superuser or member of pg_rotate_logfile to rotate log files"))));
 
 	if (!Logging_collector)
 	{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f7c9bf6..8cac7c2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/ip.h"
@@ -642,7 +643,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), beentry->st_userid) ||
+			has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		{
 			SockAddr	zero_clientaddr;
 
@@ -846,7 +848,8 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		activity = "<backend information not available>";
-	else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	else if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+			 !has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		activity = "<insufficient privilege>";
 	else if (*(beentry->st_activity) == '\0')
 		activity = "<command string not enabled>";
@@ -867,7 +870,8 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_waiting;
@@ -886,7 +890,8 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_activity_start_timestamp;
@@ -912,7 +917,8 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_xact_start_timestamp;
@@ -934,7 +940,8 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	result = beentry->st_proc_start_timestamp;
@@ -958,7 +965,8 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
@@ -1005,7 +1013,8 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
 	if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
 		PG_RETURN_NULL();
 
-	if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+	if (!has_privs_of_role(GetUserId(), beentry->st_userid) &&
+		!has_privs_of_role(GetUserId(), DEFAULT_ROLE_MONITORID))
 		PG_RETURN_NULL();
 
 	/* A zeroed client addr means we don't know */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a185749..4946903 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/vacuum.h"
@@ -71,6 +72,7 @@
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/guc_tables.h"
@@ -8249,6 +8251,11 @@ show_all_file_settings(PG_FUNCTION_ARGS)
 	MemoryContext per_query_ctx;
 	MemoryContext oldcontext;
 
+	if (!has_privs_of_role(GetUserId(), DEFAULT_ROLE_FILE_SETTINGSID))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser or member of pg_file_settings to see all configuration file settings")));
+
 	/* Check to see if caller supports us returning a tuplestore */
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
 		ereport(ERROR,
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index 2c8565e..9f79b99 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -93,10 +93,28 @@ typedef FormData_pg_authid *Form_pg_authid;
  *
  * The uppercase quantities will be replaced at initdb time with
  * user choices.
+ *
+ * If adding new default roles or changing the OIDs below, be sure to add or
+ * update the #defines which follow as appropriate.
  * ----------------
  */
 DATA(insert OID = 10 ( "POSTGRES" t t t t t t t -1 _null_ _null_));
+DATA(insert OID = 4200 ( "pg_monitor" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4201 ( "pg_backup" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4202 ( "pg_replay" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4203 ( "pg_replication" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4204 ( "pg_rotate_logfile" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4205 ( "pg_signal_backend" f t f f f f f -1 _null_ _null_));
+DATA(insert OID = 4206 ( "pg_file_settings" f t f f f f f -1 _null_ _null_));
+
+#define BOOTSTRAP_SUPERUSERID			10
 
-#define BOOTSTRAP_SUPERUSERID 10
+#define DEFAULT_ROLE_MONITORID			4200
+#define DEFAULT_ROLE_BACKUPID			4201
+#define DEFAULT_ROLE_REPLAYID			4202
+#define DEFAULT_ROLE_REPLICATIONID		4203
+#define DEFAULT_ROLE_ROTATE_LOGFILEID	4204
+#define DEFAULT_ROLE_SIGNAL_BACKENDID	4205
+#define DEFAULT_ROLE_FILE_SETTINGSID	4206
 
 #endif   /* PG_AUTHID_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 88bdc2c..aeac29a 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1523,6 +1523,148 @@ revoke select on dep_priv_test from regressuser4 cascade;
 
 set session role regressuser1;
 drop table dep_priv_test;
+-- test default roles
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_switch_xlog(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup to switch transaction log files
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog replay location
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+ pg_switch_xlog 
+----------------
+(0 rows)
+
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_backup FROM regressuser1;
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for function pg_show_all_file_settings
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+ERROR:  permission denied for relation pg_file_settings
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+ ?column? 
+----------
+        1
+(1 row)
+
+\c
+REVOKE pg_file_settings FROM regressuser1;
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog location
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view current xlog insert location
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog receive location
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_monitor or pg_backup to view last xlog replay location
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+ pg_current_xlog_location 
+--------------------------
+(0 rows)
+
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+ pg_current_xlog_insert_location 
+---------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+ pg_last_xlog_receive_location 
+-------------------------------
+(0 rows)
+
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+ pg_last_xlog_replay_location 
+------------------------------
+(0 rows)
+
+\c
+REVOKE pg_monitor FROM regressuser1;
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replay to control recovery
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+SELECT pg_xlog_replay_resume(); -- fail
+ERROR:  recovery is not in progress
+HINT:  Recovery control functions can only be executed during recovery.
+\c
+REVOKE pg_replay FROM regressuser1;
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_start_backup('abc'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT pg_stop_backup(); -- fail-no-perm
+ERROR:  must be superuser or member of pg_backup or pg_replication to run a backup
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+ERROR:  must be superuser or member of pg_replication to use replication slots
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
 -- clean up
 \c
 drop sequence x_seq;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c1837c4..a2815e2 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -932,6 +932,89 @@ revoke select on dep_priv_test from regressuser4 cascade;
 set session role regressuser1;
 drop table dep_priv_test;
 
+-- test default roles
+
+-- pg_backup
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT pg_switch_xlog(); -- fail-no-perm
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_backup TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_switch_xlog() limit 0; -- success
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_backup FROM regressuser1;
+
+-- pg_file_settings
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- fail-no-perm
+SELECT 1 FROM pg_file_settings LIMIT 1; -- fail-no-perm
+\c
+GRANT pg_file_settings TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT 1 FROM pg_show_all_file_settings() LIMIT 1; -- success
+SELECT 1 FROM pg_file_settings LIMIT 1; -- success
+\c
+REVOKE pg_file_settings FROM regressuser1;
+
+-- pg_monitor
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_current_xlog_location(); -- fail-no-perm
+SELECT pg_current_xlog_insert_location(); -- fail-no-perm
+SELECT pg_last_xlog_receive_location(); -- fail-no-perm
+SELECT pg_last_xlog_replay_location(); -- fail-no-perm
+\c
+GRANT pg_monitor TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_current_xlog_location() limit 0; -- success
+SELECT pg_current_xlog_insert_location() limit 0; -- success
+SELECT pg_last_xlog_receive_location() limit 0; -- success
+SELECT pg_last_xlog_replay_location() limit 0; -- success
+\c
+REVOKE pg_monitor FROM regressuser1;
+
+-- pg_replay
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_xlog_replay_pause(); -- fail-no-perm
+SELECT pg_xlog_replay_resume(); -- fail-no-perm
+\c
+GRANT pg_replay TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+SELECT pg_xlog_replay_pause(); -- fail
+SELECT pg_xlog_replay_resume(); -- fail
+\c
+REVOKE pg_replay FROM regressuser1;
+
+-- pg_replication
+\c
+SET SESSION AUTHORIZATION regressuser1;
+
+SELECT pg_start_backup('abc'); -- fail-no-perm
+SELECT pg_stop_backup(); -- fail-no-perm
+SELECT * FROM pg_create_physical_replication_slot('asd',true); -- fail-no-perm
+SELECT pg_drop_replication_slot('asd'); -- fail-no-perm
+\c
+GRANT pg_replication TO regressuser1;
+SET SESSION AUTHORIZATION regressuser1;
+\c
+REVOKE pg_replication FROM regressuser1;
 
 -- clean up
 
-- 
2.5.0

#137Noah Misch
noah@leadboat.com
In reply to: Stephen Frost (#136)
Re: Additional role attributes && superuser review

On Tue, Dec 22, 2015 at 05:23:47PM -0500, Stephen Frost wrote:

On Tue, Dec 22, 2015 at 1:41 AM, Stephen Frost <sfrost@snowman.net> wrote:

Updated and rebased patch attached which takes the 'pg_switch_xlog'
default role back out, leaving us with:

pg_monitor - View privileged info
pg_backup - start/stop backups, switch xlog, create restore points
pg_replay - Pause/resume xlog replay on replicas
pg_replication - Create/destroy/etc replication slots
pg_rotate_logfile - Request logfile rotation
pg_signal_backend - Signal other backends (cancel query/terminate)
pg_file_settings - View configuration settings in all config files

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

This thread and its satellite[1]/messages/by-id/20150508042928.GP30322@tamriel.snowman.net have worked their way through a few designs.
At first, it was adding role attributes, alongside existing attributes like
REPLICATION and BYPASSRLS. It switched[2]/messages/by-id/20150402045311.GW3663@tamriel.snowman.net to making pg_dump preserve ACLs on
system objects. Built-in roles joined[3]/messages/by-id/20150429144722.GY30322@tamriel.snowman.net the pg_dump work to offer predefined
collections of ACL grants. Finally, it dropped[4]/messages/by-id/20150508042928.GP30322@tamriel.snowman.net the pg_dump side and
hard-coded the roles into the features they govern.

I find pg_dump support for system object ACLs to be the best of the goals
pursued here. [5]/messages/by-id/CA+TgmobH4tdccajn7VmPT-1RqBdzLYcAz5jUz4bJ=rkqs_gADA@mail.gmail.com proposed a good division of pg_dump+roles into multiple
patches, and the pg_dump support for system object ACLs belongs as the first
of the series. There's nothing to like about today's behavior of allowing the
GRANT but omitting it from dumps, and attacking the problem at that layer will
provide considerable admin freedom. Starting there is important, because it
will change the roles design. I doubt we would add role pg_rotate_logfile if
"GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO mydba" were fully usable;
likewise for other roles that would carry a single ACL. Contrary to comments
upthread, we won't be particularly free to redefine the scope of built-in
roles later. Removing privileges from a role will be an ordinary
compatibility break, and adding privileges will be quite sensitive.

Your first patch, to catalogs.sgml, stands alone. May as well get that out of
the way. To answer one question you asked a few times, reserving the pg_ role
prefix is fine. (Whether any particular pg_foo role proposal sinks or floats
is a separate question.)

To summarize, I think the right next step is to resume designing pg_dump
support for system object ACLs. I looked over your other two patches and will
unshelve those reviews when their time comes.

Thanks,
nm

[1]: /messages/by-id/20150508042928.GP30322@tamriel.snowman.net
[2]: /messages/by-id/20150402045311.GW3663@tamriel.snowman.net
[3]: /messages/by-id/20150429144722.GY30322@tamriel.snowman.net
[4]: /messages/by-id/20150508042928.GP30322@tamriel.snowman.net
[5]: /messages/by-id/CA+TgmobH4tdccajn7VmPT-1RqBdzLYcAz5jUz4bJ=rkqs_gADA@mail.gmail.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#138Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Stephen Frost (#136)
Re: Additional role attributes && superuser review

Hi,

On 2015/12/23 7:23, Stephen Frost wrote:

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

Just a minor nitpick about a code comment -

     /*
+     * Check that the user is not trying to create a role in the reserved
+     * "pg_" namespace.
+     */
+    if (IsReservedName(stmt->role))

The wording may be slightly confusing, especially saying "... in ...
namespace". ISTM, "namespace" is fairly extensively used around the code
to mean something like "a schema's namespace".

Could perhaps be reworded as:

     /*
+     * Check that the user is not trying to create a role with reserved
+     * name, ie, one starting with "pg_".

If OK, there seems to be one more place further down in the patch with
similar wording.

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#139Stephen Frost
sfrost@snowman.net
In reply to: Noah Misch (#137)
Re: Additional role attributes && superuser review

Noah,

* Noah Misch (noah@leadboat.com) wrote:

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

This thread and its satellite[1] have worked their way through a few designs.
At first, it was adding role attributes, alongside existing attributes like
REPLICATION and BYPASSRLS. It switched[2] to making pg_dump preserve ACLs on
system objects. Built-in roles joined[3] the pg_dump work to offer predefined
collections of ACL grants. Finally, it dropped[4] the pg_dump side and
hard-coded the roles into the features they govern.

Correct, after quite a bit of discussion and the conclusion that, while
pg_dump support for dumping ACLs might be interesting, it was quite a
bit more complex an approach than this use-case justified. Further,
adding support to pg_dump for dumping ACLs could be done independently
of default roles.

The one argument which you've put forth for adding the complexity of
dumping catalog ACLs is that we might reduce the number of default
roles provided to the user. I disagree that we would. Having a single
set of default roles which provide a sensible breakdown of permissions
is a better approach than asking every administrator and application
developer who is building tools on top of PG to try and work through
what makes sense themselves, even if that means we have a default role
with a small, or even only an individual, capability.

I also disagree that we won't be able to adjust the privileges granted
to a role in the future. We have certainly made adjustments to what a
'replication' role is able to do, which has largely been in the 'more
capabilities' direction that you opine concern over.

To summarize, I think the right next step is to resume designing pg_dump
support for system object ACLs. I looked over your other two patches and will
unshelve those reviews when their time comes.

To be clear, I don't believe the two patches are particularly involved
with each other and don't feel that one needs to wait for the other.

Further, I'm not convinced that adding support for dumping ACLs or, in
general, encouraging users to define their own ACLs on catalog objects
is a good idea. We certainly have no mechanism in place today for those
ACLs to be respected by SysCache and encouraging their use when we won't
actually respect them is likely to be confusing. I had thought
differently at one point but my position changed during the discussion
when I realized the complexity and potential confusion it could cause
and considered that against the simplicity and relatively low cost of
having default roles.

Thanks!

Stephen

#140Stephen Frost
sfrost@snowman.net
In reply to: Amit Langote (#138)
Re: Additional role attributes && superuser review

Amit,

* Amit Langote (Langote_Amit_f8@lab.ntt.co.jp) wrote:

On 2015/12/23 7:23, Stephen Frost wrote:

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

Just a minor nitpick about a code comment -

/*
+     * Check that the user is not trying to create a role in the reserved
+     * "pg_" namespace.
+     */
+    if (IsReservedName(stmt->role))

The wording may be slightly confusing, especially saying "... in ...
namespace". ISTM, "namespace" is fairly extensively used around the code
to mean something like "a schema's namespace".

Could perhaps be reworded as:

/*
+     * Check that the user is not trying to create a role with reserved
+     * name, ie, one starting with "pg_".

If OK, there seems to be one more place further down in the patch with
similar wording.

I could go either way on that, really. I don't find namespace to be
confusing when used in that way, but I'll change it since others do.

Thanks!

Stephen

#141Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#140)
Re: Additional role attributes && superuser review

On Tue, Dec 29, 2015 at 11:55 PM, Stephen Frost <sfrost@snowman.net> wrote:

I could go either way on that, really. I don't find namespace to be
confusing when used in that way, but I'll change it since others do.

It seems to me that the way patch does it is fine..
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#142Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#139)
Re: Additional role attributes && superuser review

On Tue, Dec 29, 2015 at 5:35 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Noah Misch (noah@leadboat.com) wrote:

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

This thread and its satellite[1] have worked their way through a few designs.
At first, it was adding role attributes, alongside existing attributes like
REPLICATION and BYPASSRLS. It switched[2] to making pg_dump preserve ACLs on
system objects. Built-in roles joined[3] the pg_dump work to offer predefined
collections of ACL grants. Finally, it dropped[4] the pg_dump side and
hard-coded the roles into the features they govern.

Correct, after quite a bit of discussion and the conclusion that, while
pg_dump support for dumping ACLs might be interesting, it was quite a
bit more complex an approach than this use-case justified.

Hmm. I don't think I agree with that conclusion. Who were the
participants in that discussion, and how many people spoke in favor
from moving on from that proposal - which I rather liked - to what
you've got now? Do you have links to the relevant portion of the
relevant thread?

I think it's not a very good thing if we add roles that allow, say,
execution of exactly one SQL function. The
dump-grants-on-system-objects proposal would accomplish the same thing
in a much more flexible way, and with less catalog clutter. More
broadly, I'm not very convinced that even the roles which allow for
rights on multiple objects are going to meet with general approval.
There has been enough discussion of which roles should be created and
which things should be included in each one, and the overall tenor of
that discussion seems to be that different people have different
ideas. Under those circumstances, it seems very dubious to proceed
with this. Michael seems to think that we can go ahead and start
changing things and sort out whatever is broken later, but that
doesn't sound like a very good plan to me. We can change the
internals of a bad implementation later and replace it with a good
implementation, but changing user-visible behavior once people have
started relying on it is a lot harder.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#143Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#142)
Re: Additional role attributes && superuser review

On Thu, Dec 31, 2015 at 1:50 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Under those circumstances, it seems very dubious to proceed
with this. Michael seems to think that we can go ahead and start
changing things and sort out whatever is broken later, but that
doesn't sound like a very good plan to me.

I meant [snip]tuning those roles during this development cycle.[/snip]
But I'll just withdraw as there are enough concerns, from two
committers on top of it. My point was just to move on with this patch
in the direction where the overall consensus seemed to be at the point
I begun participating in this thread.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#144Noah Misch
noah@leadboat.com
In reply to: Stephen Frost (#139)
Re: Additional role attributes && superuser review

On Tue, Dec 29, 2015 at 08:35:50AM -0500, Stephen Frost wrote:

* Noah Misch (noah@leadboat.com) wrote:

The one argument which you've put forth for adding the complexity of
dumping catalog ACLs is that we might reduce the number of default
roles provided to the user.

Right. If "GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO mydba" worked as
well as it works on user-defined functions, the community would not choose to
add a pg_rotate_logfile role holding just that one permission.

I disagree that we would. Having a single
set of default roles which provide a sensible breakdown of permissions
is a better approach than asking every administrator and application
developer who is building tools on top of PG to try and work through
what makes sense themselves, even if that means we have a default role
with a small, or even only an individual, capability.

The proposed pg_replication role introduces abstraction that could, as you
hope, spare a DBA from studying sets of functions to grant together. The
pg_rotate_logfile role, however, does not shield the DBA from complexity.
Being narrowly tied to a specific function, it's just a suboptimal spelling of
GRANT. The gap in GRANT has distorted the design for these predefined roles.
I do not anticipate a sound design discussion about specific predefined roles
so long as the state of GRANT clouds the matter.

To summarize, I think the right next step is to resume designing pg_dump
support for system object ACLs. I looked over your other two patches and will
unshelve those reviews when their time comes.

To be clear, I don't believe the two patches are particularly involved
with each other and don't feel that one needs to wait for the other.

Patch 2/3 could stand without patch 3/3, but not vice-versa. It's patch 2/3
that makes pg_dumpall skip ^pg_ roles, and that must be in place no later than
the first patch that adds a predefined ^pg_ role.

Further, I'm not convinced that adding support for dumping ACLs or, in
general, encouraging users to define their own ACLs on catalog objects
is a good idea. We certainly have no mechanism in place today for those
ACLs to be respected by SysCache and encouraging their use when we won't
actually respect them is likely to be confusing.

What's this problem with syscache? It sounds important.

nm

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#145Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#141)
Re: Additional role attributes && superuser review

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Tue, Dec 29, 2015 at 11:55 PM, Stephen Frost <sfrost@snowman.net> wrote:

I could go either way on that, really. I don't find namespace to be
confusing when used in that way, but I'll change it since others do.

It seems to me that the way patch does it is fine..

Alright, fair enough.

Thanks!

Stephen

#146Michael Paquier
michael.paquier@gmail.com
In reply to: Noah Misch (#144)
Re: Additional role attributes && superuser review

On Thu, Dec 31, 2015 at 4:26 PM, Noah Misch <noah@leadboat.com> wrote:

On Tue, Dec 29, 2015 at 08:35:50AM -0500, Stephen Frost wrote:

* Noah Misch (noah@leadboat.com) wrote:
I disagree that we would. Having a single
set of default roles which provide a sensible breakdown of permissions
is a better approach than asking every administrator and application
developer who is building tools on top of PG to try and work through
what makes sense themselves, even if that means we have a default role
with a small, or even only an individual, capability.

The proposed pg_replication role introduces abstraction that could, as you
hope, spare a DBA from studying sets of functions to grant together. The
pg_rotate_logfile role, however, does not shield the DBA from complexity.
Being narrowly tied to a specific function, it's just a suboptimal spelling of
GRANT. The gap in GRANT has distorted the design for these predefined roles.
I do not anticipate a sound design discussion about specific predefined roles
so long as the state of GRANT clouds the matter.

As the patch stands, the system roles that are just close to synonyms
of GRANT/REVOKE are the following:
- pg_file_settings, which works just on the system view
pg_file_settings and the function pg_show_all_file_settings().
- pg_rotate_logfile as mentioned already.
- pg_signal_backend, which is just checked once in pg_signal_backend
Based on those concerns, it looks clear that they should be shaved off
from the patch.

To summarize, I think the right next step is to resume designing pg_dump
support for system object ACLs. I looked over your other two patches and will
unshelve those reviews when their time comes.

To be clear, I don't believe the two patches are particularly involved
with each other and don't feel that one needs to wait for the other.

Patch 2/3 could stand without patch 3/3, but not vice-versa. It's patch 2/3
that makes pg_dumpall skip ^pg_ roles, and that must be in place no later than
the first patch that adds a predefined ^pg_ role.

I am a bit confused by this statement, and I disagree with Stephen's
point as well. It seems to me that 2/3 exists *because* 3/3 is here.
Why would we want to restrict the role names that can be used if we
are not going to introduce some system roles that are created at
bootstrap?
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#147Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#146)
Re: Additional role attributes && superuser review

Based on the feedback here, I have returned this patch to Needs Review
status. (Waiting on Author would be fairer actually, since we are
waiting for an updated version.)

As far as I can make it from Noah and Robert's comments, what we would
like to see here is a way for pg_dump to output nondefault grants to
system objects; that would solve part of the need here where this patch
proposes default roles for one or two system objects. Another part of
this patch, which could presumably be committed independently, is the
addition of default roles that are related to larger sets of system
objects.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#148Stephen Frost
sfrost@snowman.net
In reply to: Noah Misch (#144)
Re: Additional role attributes && superuser review

Noah,

* Noah Misch (noah@leadboat.com) wrote:

On Tue, Dec 29, 2015 at 08:35:50AM -0500, Stephen Frost wrote:

* Noah Misch (noah@leadboat.com) wrote:

The one argument which you've put forth for adding the complexity of
dumping catalog ACLs is that we might reduce the number of default
roles provided to the user.

Right. If "GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO mydba" worked as
well as it works on user-defined functions, the community would not choose to
add a pg_rotate_logfile role holding just that one permission.

I understand that's your position, but I disagree with your conclusion.

If we're going to provide default roles, which I continue to feel is a
good approach, then I would suggest we use them as an abstraction for
the permissions which we view as senible sets of grantable rights. I
dislike the idea of having default roles and then making an exception
for single-permission cases.

I'm approaching this largely from a 3rd-party application perspective.
There are two examples off-hand which I'm considering:

check_postgres
pgbackrest

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

check_postgres:

Most check_postgres sub-commands can be run without superuser
privileges by granting the pg_monitor role to the monitoring user:

GRANT pg_monitor TO monitor;

For information regarding the pg_monitor role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

GRANT pg_backup to backrest;

For information regarding the pg_backup role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

I can see similar bits of documentation being included in pgAdmin or
other tools. For the pg_rotate_logfile permission, specifically, we
were asked by a client about that permission with the use case being a
logrotate-type of tool, which only has access to the log files but needs
to be able to perform a rotation. This particular client is pretty tech
savvy and I don't think they'd have a problem using GRANT EXECUTE if
that was the only option, but I can see a similar use-case with
logrotate or pgAdmin or even for regular non-superuser admins using psql
and, to reiterate what I said above, I'd rather have one abstraction for
these kinds of permissions instead of a mish-mash of instructions. The
difference I can imagine being between:

For backups and monitoring, you can use default roles:

GRANT pg_backup,pg_monitor to new_admin;

but for other regular privileges such as rotating logfiles, or sending
signals to other processes, you have to explicitly GRANT permissions:

GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO new_admin;
GRANT EXECUTE ON FUNCTION pg_signal_backend() TO new_admin;

I disagree that we would. Having a single
set of default roles which provide a sensible breakdown of permissions
is a better approach than asking every administrator and application
developer who is building tools on top of PG to try and work through
what makes sense themselves, even if that means we have a default role
with a small, or even only an individual, capability.

The proposed pg_replication role introduces abstraction that could, as you
hope, spare a DBA from studying sets of functions to grant together. The
pg_rotate_logfile role, however, does not shield the DBA from complexity.

I disagree with the above statement. Having to understand only one
level of abstraction (the default roles) does reduce the complexity and
means that the DBA does not have to work out if the specifc GRANT
requested by the end user would result in some other access or if there
are any unexpected issues to encounter with issuing GRANTs directly on
catalog objects- something we don't currently support, so such concern
is certainly reasonable.

Being narrowly tied to a specific function, it's just a suboptimal spelling of
GRANT. The gap in GRANT has distorted the design for these predefined roles.
I do not anticipate a sound design discussion about specific predefined roles
so long as the state of GRANT clouds the matter.

I'm loathe to encourage any direct modification of catalog objects,
even if it's just ACLs. I've seen too many cases, as I imagine others
have, of users destroying their databases or running into unexpected
results when modifying the catalog. The catalog modifications supported
should be explicitly provided through other means rather than direct
commands against the catalog objects. I see the default roles approach
as being similar to having:

ALTER DATABASE db WITH CONNECTION LIMIT 5;

instead of suggesting users issue:

UPDATE DATABASE SET datconnlimit = 5 WHERE datname = 'db';

There is little difference between the two, technically, but I'm a whole
lot more comfortable with the ALTER DATABASE than with the user issuing
an UPDATE against a catalog table. With 9.5, we are adding
ALLOW_CONNECTIONS and IS_TEMPLATE also and I don't recall any particular
concern that those are overly redundant with the equivilant UPDATE
statement.

To summarize, I think the right next step is to resume designing pg_dump
support for system object ACLs. I looked over your other two patches and will
unshelve those reviews when their time comes.

To be clear, I don't believe the two patches are particularly involved
with each other and don't feel that one needs to wait for the other.

Patch 2/3 could stand without patch 3/3, but not vice-versa. It's patch 2/3
that makes pg_dumpall skip ^pg_ roles, and that must be in place no later than
the first patch that adds a predefined ^pg_ role.

Apologies for not being clear on this- I was referring to pg_dump
support for GRANT on catalog objects vs. the default roles patch in my
statement by way of summary.

Further, I'm not convinced that adding support for dumping ACLs or, in
general, encouraging users to define their own ACLs on catalog objects
is a good idea. We certainly have no mechanism in place today for those
ACLs to be respected by SysCache and encouraging their use when we won't
actually respect them is likely to be confusing.

What's this problem with syscache? It sounds important.

CREATE TABLE and DROP TABLE aren't going to care one bit if you have
access to pg_class or not. The same goes for basically everything else.

If we really want to support ACLs on the catalog, we'd have to either
caveat that none of the internal lookups will respect them or revamp
SysCache and any other direct catalog access to do permission checks
first, which I don't think we really want to do.

This entire discussion of privileges-on-catalog-objects should really
also consider the ongoing discussion about providing policies for the
catalog via RLS. If we start pg_dump'ing the ACLs of catalog objects
then we'd, presumably, also want to pg_dump out any policies defined
against catalog objects. I wonder if we may end up causing ourselves
trouble going with that approach though if we start providing a set of
default policies which SysCache knows how to work with but which users
can change.

Thanks!

Stephen

#149Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#142)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Tue, Dec 29, 2015 at 5:35 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Noah Misch (noah@leadboat.com) wrote:

Updated patch attached. I'll give it another good look and then commit
it, barring objections.

This thread and its satellite[1] have worked their way through a few designs.
At first, it was adding role attributes, alongside existing attributes like
REPLICATION and BYPASSRLS. It switched[2] to making pg_dump preserve ACLs on
system objects. Built-in roles joined[3] the pg_dump work to offer predefined
collections of ACL grants. Finally, it dropped[4] the pg_dump side and
hard-coded the roles into the features they govern.

Correct, after quite a bit of discussion and the conclusion that, while
pg_dump support for dumping ACLs might be interesting, it was quite a
bit more complex an approach than this use-case justified.

Hmm. I don't think I agree with that conclusion. Who were the
participants in that discussion, and how many people spoke in favor
from moving on from that proposal - which I rather liked - to what
you've got now? Do you have links to the relevant portion of the
relevant thread?

I'm not sure it's entirely relevant now- I've outlined the reasoning in
my email to Noah as a, hopefully, pretty comprehensive summary. If that
doesn't sway your minds then it seems unlikely that a reference to a
thread from 6 months or a year ago would. Further, as happens, other
discussions were in person where I discussed the ideas with other
hackers at conferences. I got generally positive responses to the idea
of default roles with specific sets of rights, which is the path that
I've been on since. As with most decisions, there was not a formal vote
over the proposals for me to reference. I do specifically recall the
opinion that sets of privileges probably make more sense than granting
out individual ones, from Tom, if I'm remembering correctly.

In any case, I can work on the pg_dump support for catalog ACLs if there
is agreement now on that being the direction to go in. If there's
agreement that we are happy with the idea of default roles also then I
can strip out those few which are only one-permission and which
therefore wouldn't be necessary, if we had ACL support on catalog
objects, quite easily.

I think it's not a very good thing if we add roles that allow, say,
execution of exactly one SQL function. The
dump-grants-on-system-objects proposal would accomplish the same thing
in a much more flexible way, and with less catalog clutter.

For my 2c, I don't see a default role or two as creating terribly much
clutter. Changing all of our code that currently has internal checks to
rely on the external checks and adjusting the permissions on the
individual functions will be a fair bit of churn though.

More
broadly, I'm not very convinced that even the roles which allow for
rights on multiple objects are going to meet with general approval.

There's been rather little oposition to the idea and support when I've
discussed it. Of course, that was before it got to the point where I
was planning to commit it. Perhaps there will be once it's actually in,
or maybe not until it's in the wild. In any case, I continue to feel,
as others have, that we can make adjustments moving forward.

There has been enough discussion of which roles should be created and
which things should be included in each one, and the overall tenor of
that discussion seems to be that different people have different
ideas.

Michael had a question about pg_switch_xlog, but he appeared to
reconsider that position after the subsequent discussion, which put us
back to essentially the same proposal that I started with, I believe. I
don't recall terribly much other discussion or concern about what roles
should be created or what should be included in each one, though I'd be
happy to hear your thoughts on what you'd like to see.

I certainly don't like the idea of punting on all of this to the user to
figure out, even if it does meet the specific requirements our clients
have asked for, it strikes me that we can do better.

Thanks!

Stephen

#150Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#146)
Re: Additional role attributes && superuser review

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Thu, Dec 31, 2015 at 4:26 PM, Noah Misch <noah@leadboat.com> wrote:

The proposed pg_replication role introduces abstraction that could, as you
hope, spare a DBA from studying sets of functions to grant together. The
pg_rotate_logfile role, however, does not shield the DBA from complexity.
Being narrowly tied to a specific function, it's just a suboptimal spelling of
GRANT. The gap in GRANT has distorted the design for these predefined roles.
I do not anticipate a sound design discussion about specific predefined roles
so long as the state of GRANT clouds the matter.

As the patch stands, the system roles that are just close to synonyms
of GRANT/REVOKE are the following:
- pg_file_settings, which works just on the system view
pg_file_settings and the function pg_show_all_file_settings().
- pg_rotate_logfile as mentioned already.

The above are fine, I believe.

- pg_signal_backend, which is just checked once in pg_signal_backend

This is a bit more complicated. pg_signal_backend() isn't directly
exposed, pg_cancel_backend() and pg_termiante_backend() are. I'm not
saying that doesn't mean we should, necessairly, keep the
pg_signal_backend role, just that it's more than just a single function.

Further, pg_terminate_backend and pg_cancel_backend are callable by
anyone today. We'd have to invent new functions or change user-visible
behavior in an unfortunate way- we don't want *anyone* to be able to
call those functions on *anyone* unless they've been specifically
granted that access. Today, you can cancel and/or terminate your own
backends and superusers can cancel and/or termiante anyone's. The point
of pg_signal_backend was to allow non-superusers to cancel and/or
terminate any non-superuser-started backends. With the default role
approach, we can provide exactly the intended semantics and not break
backwards compatibility for those functions.

Based on those concerns, it looks clear that they should be shaved off
from the patch.

I'd like to hear back regarding the summary email that I sent to Noah
and the follow-up I sent to Robert, as they have time to reply, of
course. It's certainly easy enough to remove those default roles if
that's the consensus though.

To summarize, I think the right next step is to resume designing pg_dump
support for system object ACLs. I looked over your other two patches and will
unshelve those reviews when their time comes.

To be clear, I don't believe the two patches are particularly involved
with each other and don't feel that one needs to wait for the other.

Patch 2/3 could stand without patch 3/3, but not vice-versa. It's patch 2/3
that makes pg_dumpall skip ^pg_ roles, and that must be in place no later than
the first patch that adds a predefined ^pg_ role.

I am a bit confused by this statement, and I disagree with Stephen's
point as well. It seems to me that 2/3 exists *because* 3/3 is here.
Why would we want to restrict the role names that can be used if we
are not going to introduce some system roles that are created at
bootstrap?

My comment was, evidently, not anywhere near clear enough. The two
patches I was referring to were the pg_dump-support-for-catalog-ACLs and
the default-roles patches. Apologies for the confusion there.

Thanks!

Stephen

#151Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#148)
Re: Additional role attributes && superuser review

On Mon, Jan 4, 2016 at 12:55 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

check_postgres:

Most check_postgres sub-commands can be run without superuser
privileges by granting the pg_monitor role to the monitoring user:

GRANT pg_monitor TO monitor;

For information regarding the pg_monitor role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

GRANT pg_backup to backrest;

For information regarding the pg_backup role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

So I have two comments on this.

First, it's not really going to matter to users very much whether the
command to enable one of these features is a single GRANT command or a
short sequence of GRANT commands executed one after another. So even
if we don't have roles that bundle together multiple permissions, you
will still be able to do this; you just might need more than one
command. Probably, I'm guessing, not very many commands, but more
than one.

Second, I think it's really unlikely that you're going to maintain
perfect alignment between your predefined roles and the permissions
that third-party tools need over the course of multiple releases. I
think the chances of that working out are just about zero. Sure, you
can make the initial set of permissions granted to pg_backup match
exactly what pgbackrest needs, but it's a good bet that one of the six
or eight widely-used backup tools uses something that's not included
in that set. And even if not, it doesn't require very much
imagination to suppose that some tool, maybe pgbackrest or maybe
something else, that comes along over the next few releases will
require some extra permission. When that happens, will we include add
that permission to the pg_backup role, or not? If we do, then we'll
be giving excess permissions to all the other backup tools that don't
need that new right, and maybe surprising users who upgrade without
realizing that some of their roles now have new rights. If we don't,
then that tool won't be able to work without some additional
twiddling. I just find it incredibly unlikely that every monitoring
tool, or every backup tool, or every admin tool will require exactly
the same set of permissions.

I can see similar bits of documentation being included in pgAdmin or
other tools.

...and pgAdmin is a particularly good example. The permissions that
pgAdmin requires depend on what you want to do with it, and it does a
lot of things, and it might do more or fewer things in the future.
You can't even fairly assume that everyone wants to give the same
permissions to pgAdmin, let alone that some competing tool will
require the same set.

Being narrowly tied to a specific function, it's just a suboptimal spelling of
GRANT. The gap in GRANT has distorted the design for these predefined roles.
I do not anticipate a sound design discussion about specific predefined roles
so long as the state of GRANT clouds the matter.

I'm loathe to encourage any direct modification of catalog objects,
even if it's just ACLs. I've seen too many cases, as I imagine others
have, of users destroying their databases or running into unexpected
results when modifying the catalog. The catalog modifications supported
should be explicitly provided through other means rather than direct
commands against the catalog objects. I see the default roles approach
as being similar to having:

ALTER DATABASE db WITH CONNECTION LIMIT 5;

instead of suggesting users issue:

UPDATE DATABASE SET datconnlimit = 5 WHERE datname = 'db';

This doesn't make any sense to me. Nobody was proposing issuing an
UPDATE against pg_database directly (and it would have to be
pg_database, not just database as you wrote here). We're talking
about whether the user is going to GRANT pg_rotate_logfile TO ... or
GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO ... which is not the
same sort of thing at all.

What's this problem with syscache? It sounds important.

CREATE TABLE and DROP TABLE aren't going to care one bit if you have
access to pg_class or not. The same goes for basically everything else.

If we really want to support ACLs on the catalog, we'd have to either
caveat that none of the internal lookups will respect them or revamp
SysCache and any other direct catalog access to do permission checks
first, which I don't think we really want to do.

This doesn't make any sense to me, either. Calling a function from
the SQL checks the permissions on that function. This works just fine
today:

rhaas=# revoke execute on function pg_rotate_logfile() from public;
REVOKE
rhaas=# set role bob;
SET
rhaas=> select pg_rotate_logfile();
ERROR: permission denied for function pg_rotate_logfile

We can do that by default and it will Just Work. All we need to have
a complete solution here is (1) remove the superuser check from the C
code and (2) make pg_dump dump and restore the ACL properly.

Syscaches really have nothing to do with this.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#152Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#149)
Re: Additional role attributes && superuser review

On Mon, Jan 4, 2016 at 3:07 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'm not sure it's entirely relevant now- I've outlined the reasoning in
my email to Noah as a, hopefully, pretty comprehensive summary. If that
doesn't sway your minds then it seems unlikely that a reference to a
thread from 6 months or a year ago would. Further, as happens, other
discussions were in person where I discussed the ideas with other
hackers at conferences. I got generally positive responses to the idea
of default roles with specific sets of rights, which is the path that
I've been on since. As with most decisions, there was not a formal vote
over the proposals for me to reference. I do specifically recall the
opinion that sets of privileges probably make more sense than granting
out individual ones, from Tom, if I'm remembering correctly.

To be honest, that's not really my point. You have cited off-list
discussions you've had over and over as a reason why you proceeded
down some particular path. That is fine up to a point - I discuss
lots of things with people off-list, too - but a consensus that a
particular design is acceptable for commit has to mean the consensus
on this mailing list, and nothing else. In seven years of reading
this mailing list, I can't remember a single other person on this
mailing list saying "I'm going to commit this because I talked to a
bunch of unspecified people at conferences I attended and they liked
it", but you've used essentially that rationale a couple of times now.

For my 2c, I don't see a default role or two as creating terribly much
clutter.

I don't believe any version of this proposal has involved only one or
two such roles. They've all had at least four or five AFAICS. Now
maybe that's still OK, but 4 or 5 > 1 or 2, and the number is only
likely to go up in the future.

Changing all of our code that currently has internal checks to
rely on the external checks and adjusting the permissions on the
individual functions will be a fair bit of churn though.

I'm not sure it'll really be that bad, but if you have some evidence
that I'm wrong I'd like to hear it.

More
broadly, I'm not very convinced that even the roles which allow for
rights on multiple objects are going to meet with general approval.

There's been rather little oposition to the idea and support when I've
discussed it. Of course, that was before it got to the point where I
was planning to commit it. Perhaps there will be once it's actually in,
or maybe not until it's in the wild. In any case, I continue to feel,
as others have, that we can make adjustments moving forward.

So, is this another case where the support is all in off-list fora and
thus invisible, or can you point to specific on-list discussions where
it was supported, and to the opinions offered in support? I don't
really remember many opinions that were any more positive than "I
wouldn't be strongly opposed to this" or "If we're going to do this
then we ought to do it in X way". I'm happy to be corrected if I'm
misrepresenting the record, but I'd characterize the overall reaction
to this proposal as tepid: nobody hated it, but nobody really loved it
either, and a bunch of mild concerns were offered.

There has been enough discussion of which roles should be created and
which things should be included in each one, and the overall tenor of
that discussion seems to be that different people have different
ideas.

Michael had a question about pg_switch_xlog, but he appeared to
reconsider that position after the subsequent discussion, which put us
back to essentially the same proposal that I started with, I believe. I
don't recall terribly much other discussion or concern about what roles
should be created or what should be included in each one, though I'd be
happy to hear your thoughts on what you'd like to see.

Honestly, my vote is for creating only those predefined roles that are
clearly endorsed by three people with different employers, which I
currently believe to be true of none of the proposed roles. It's not
even that I suspect you or anyone of ballot-stuffing; it's just that
people who work at different companies are likely to work with
different tools and those tools may have different requirements. I
mean, people at 2ndQuadrant probably mostly use repmgr; people at
Crunchy probably like pgbackrest; people at OmniTI probably use
PITRtools; and EnterpriseDB employees are more likely than average to
be familiar with BART. If several of those people come together and
say they all agree that predefined role X is perfect for the needs of
all of their respective tools, I'd consider that a really good sign
that we've hit on something that is of general utility. Otherwise,
I'd just the authors of each tool specify the GRANT EXECUTE ON
FUNCTION lines necessary for their own tool and call it good. I think
that's almost as convenient and a lot more flexible.

What really bothers me about this thread is that these predefined
roles are intended to be useful for third-party tools, but the people
who maintain those third-party tools have said basically nothing. I
don't recall, for example, Dave Page weighing in on what pgAdmin
needs, or anybody commenting on to what degree any of these proposals
would meet the needs of Slony or pgBouncer or pgPool or any backup
tool (other than perhaps pgbackrest, which I assume your proposals
cater to) or any monitoring tool. Like, we've heard zip. Either
those people don't know this thread exists, or they can't understand
it, or they think it's so boring that they can't be bothered to write
in and say whether this is useful or not. I'd have a lot more
confidence that we are making a good decision if some of those people
would show up and say "I have reviewed this proposal and it looks {
great | terrible | mediocre } for $TOOL because $REASON".

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#153Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#151)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 4, 2016 at 12:55 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

check_postgres:

Most check_postgres sub-commands can be run without superuser
privileges by granting the pg_monitor role to the monitoring user:

GRANT pg_monitor TO monitor;

For information regarding the pg_monitor role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

GRANT pg_backup to backrest;

For information regarding the pg_backup role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

So I have two comments on this.

First, it's not really going to matter to users very much whether the
command to enable one of these features is a single GRANT command or a
short sequence of GRANT commands executed one after another. So even
if we don't have roles that bundle together multiple permissions, you
will still be able to do this; you just might need more than one
command. Probably, I'm guessing, not very many commands, but more
than one.

I disagree. I find that it does make a difference to users to be using
a well documented and clear approach over one which involves fiddling
with the individual pieces to get everything to work, and if a new
function comes along that is useful for backup users, that would have to
also be granted, even if it would clearly be useful to a backup role.

Second, I think it's really unlikely that you're going to maintain
perfect alignment between your predefined roles and the permissions
that third-party tools need over the course of multiple releases. I
think the chances of that working out are just about zero. Sure, you
can make the initial set of permissions granted to pg_backup match
exactly what pgbackrest needs, but it's a good bet that one of the six
or eight widely-used backup tools uses something that's not included
in that set.

There may be something I've missed with the proposed pg_backup role, but
I don't believe you're correct that there isn't a set of privileges
which all of those backup tools need and which could be provided through
the pg_backup role.

And even if not, it doesn't require very much
imagination to suppose that some tool, maybe pgbackrest or maybe
something else, that comes along over the next few releases will
require some extra permission. When that happens, will we include add
that permission to the pg_backup role, or not? If we do, then we'll
be giving excess permissions to all the other backup tools that don't
need that new right, and maybe surprising users who upgrade without
realizing that some of their roles now have new rights. If we don't,
then that tool won't be able to work without some additional
twiddling. I just find it incredibly unlikely that every monitoring
tool, or every backup tool, or every admin tool will require exactly
the same set of permissions.

As I pointed out previously, we've already been doing this with the
replication role attribute and I don't recall any complaining about it.

This discussion started out with the idea of adding more role
attributes to address this need to break out superuser rights, as we
have done in the past, and then moved to discussing default roles
instead because it's a better solution for abstracting permissions as
roles are more easily grantable, delegation of role granting can be
done, and role membership works with inheritance.

The arguments you raise here would apply nearly the same to the
replication role attribute, but in practice, we don't seem to have any
question regarding how that's handled, nor have we gotten complaints
about it. I expect the same would be true with default roles and don't
see any particular reason to fear otherwise.

I can see similar bits of documentation being included in pgAdmin or
other tools.

...and pgAdmin is a particularly good example. The permissions that
pgAdmin requires depend on what you want to do with it, and it does a
lot of things, and it might do more or fewer things in the future.
You can't even fairly assume that everyone wants to give the same
permissions to pgAdmin, let alone that some competing tool will
require the same set.

I wasn't suggesting that we give everyone the same privileges to some
default 'pgAdmin' role but rather that providing an abstraction of the
set of privileges possible against the catalog objects, into sets that
make sense together, is a useful simplification for users and that it'd
be a better approach than asking users to figure out what sets make
sense on their own.

Being narrowly tied to a specific function, it's just a suboptimal spelling of
GRANT. The gap in GRANT has distorted the design for these predefined roles.
I do not anticipate a sound design discussion about specific predefined roles
so long as the state of GRANT clouds the matter.

I'm loathe to encourage any direct modification of catalog objects,
even if it's just ACLs. I've seen too many cases, as I imagine others
have, of users destroying their databases or running into unexpected
results when modifying the catalog. The catalog modifications supported
should be explicitly provided through other means rather than direct
commands against the catalog objects. I see the default roles approach
as being similar to having:

ALTER DATABASE db WITH CONNECTION LIMIT 5;

instead of suggesting users issue:

UPDATE DATABASE SET datconnlimit = 5 WHERE datname = 'db';

This doesn't make any sense to me. Nobody was proposing issuing an
UPDATE against pg_database directly (and it would have to be
pg_database, not just database as you wrote here). We're talking
about whether the user is going to GRANT pg_rotate_logfile TO ... or
GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO ... which is not the
same sort of thing at all.

I was showing them broadly under "making changes to catalog objects."
Granting a default role isn't making a modification to a catalog object
at all and would be explicitly supported. GRANT'ing EXECUTE on a
catalog function works today, but isn't really supported. The pg_dump
change would be explicitly changing that to be a supported operation.

What's this problem with syscache? It sounds important.

CREATE TABLE and DROP TABLE aren't going to care one bit if you have
access to pg_class or not. The same goes for basically everything else.

If we really want to support ACLs on the catalog, we'd have to either
caveat that none of the internal lookups will respect them or revamp
SysCache and any other direct catalog access to do permission checks
first, which I don't think we really want to do.

This doesn't make any sense to me, either. Calling a function from
the SQL checks the permissions on that function. This works just fine
today:

Yes, it does, I didn't mean to imply otherwise. The above discussion
was in regards to SysCache, as used internally by various commands.

We can do that by default and it will Just Work. All we need to have
a complete solution here is (1) remove the superuser check from the C
code and (2) make pg_dump dump and restore the ACL properly.

Syscaches really have nothing to do with this.

Adding pg_dump dump and restore support for catalog ACLs implies that
we're supporting ACLs on all catalog objects- including tables.
Otherwise, we're going to have to figure out exactly what objects we
allow setting ACLs on and which ones we don't.

REVOKE'ing SELECT access to pg_class isn't going to make DROP TABLE
stop working, even though it clearly is going to be accessing pg_class.
That's the point which I was attempting to make with regard to SysCache.

Another consideration is that the catalog changes pretty regularly and
we'd have to make sure that we are dumping out the correct set of
privileges and applying them correctly to the new catalog- and that's
only going to work reliably if the user is using the newer version of
pg_dump; we'll have no hope if they're using an old version. While
that's what we've always recommended, restoring an old dump into a newer
version, by-and-large, does work today and is certainly commonly done.

I'm not excited about the complaints we'd get if we implemented a change
which reliably broke that and I'm afraid this might and we wouldn't
realize it until we're on our second major release with that capability
and have already signed off on supporting catalog ACLs.

These complications and considerations are exactly the kind of
additional complexity that adding pg_dump dump/restore support for
catalog ACLs implies and that's why, while it sounds pretty interesting,
I don't believe it's necessairly a good idea. The default roles
approach is quite simple and similar to, while being better than, the
existing role attributes approach, in my view.

Thanks!

Stephen

#154Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#152)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 4, 2016 at 3:07 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'm not sure it's entirely relevant now- I've outlined the reasoning in
my email to Noah as a, hopefully, pretty comprehensive summary. If that
doesn't sway your minds then it seems unlikely that a reference to a
thread from 6 months or a year ago would. Further, as happens, other
discussions were in person where I discussed the ideas with other
hackers at conferences. I got generally positive responses to the idea
of default roles with specific sets of rights, which is the path that
I've been on since. As with most decisions, there was not a formal vote
over the proposals for me to reference. I do specifically recall the
opinion that sets of privileges probably make more sense than granting
out individual ones, from Tom, if I'm remembering correctly.

To be honest, that's not really my point. You have cited off-list
discussions you've had over and over as a reason why you proceeded
down some particular path. That is fine up to a point - I discuss
lots of things with people off-list, too - but a consensus that a
particular design is acceptable for commit has to mean the consensus
on this mailing list, and nothing else. In seven years of reading
this mailing list, I can't remember a single other person on this
mailing list saying "I'm going to commit this because I talked to a
bunch of unspecified people at conferences I attended and they liked
it", but you've used essentially that rationale a couple of times now.

I've found consensus among folks on the lists for far more of my commits
than I've cited off-list discussions for. Further, consensus on these
lists is largely in the quiet, which is why I go out of my way to
attempt to engage parties who may be interested when there *isn't* much
discussion on the lists. I'm trying to do better than simply assuming
consensus based on no feedback to the contrary. Had I assumed minimal
response meant consensus for this patch, as is typically the norm, this
patch would have been committed six months ago. Instead, I tried to
engage people at conferences to ensure that there really was consensus
on the approach.

For my 2c, I don't see a default role or two as creating terribly much
clutter.

I don't believe any version of this proposal has involved only one or
two such roles. They've all had at least four or five AFAICS. Now
maybe that's still OK, but 4 or 5 > 1 or 2, and the number is only
likely to go up in the future.

The 'role or two' was under the expectation that we'd still have default
roles for the more complicated cases (pg_backup, et al) and that we
would only be removing the default role of pg_switch_xlog and
pg_file_settings (the only two which an individual GRANT could replace).

Changing all of our code that currently has internal checks to
rely on the external checks and adjusting the permissions on the
individual functions will be a fair bit of churn though.

I'm not sure it'll really be that bad, but if you have some evidence
that I'm wrong I'd like to hear it.

I implemented the discussed pg_dump support for ACLs and looked at the
changes required. I may not be remembering it entirely, but it's not
that I've not looked at it.

More
broadly, I'm not very convinced that even the roles which allow for
rights on multiple objects are going to meet with general approval.

There's been rather little oposition to the idea and support when I've
discussed it. Of course, that was before it got to the point where I
was planning to commit it. Perhaps there will be once it's actually in,
or maybe not until it's in the wild. In any case, I continue to feel,
as others have, that we can make adjustments moving forward.

So, is this another case where the support is all in off-list fora and
thus invisible, or can you point to specific on-list discussions where
it was supported, and to the opinions offered in support? I don't
really remember many opinions that were any more positive than "I
wouldn't be strongly opposed to this" or "If we're going to do this
then we ought to do it in X way". I'm happy to be corrected if I'm
misrepresenting the record, but I'd characterize the overall reaction
to this proposal as tepid: nobody hated it, but nobody really loved it
either, and a bunch of mild concerns were offered.

I agree that this has largely been the on-list reaction. To be fair,
it's been largely the off-list reaction also, which I've expressly
tried to seek out, as mentioned above. I'm not asking anyone to love
it, I'm not entirely convinced it's lovable myself, but I do feel it's
useful and worth making an effort for.

There has been enough discussion of which roles should be created and
which things should be included in each one, and the overall tenor of
that discussion seems to be that different people have different
ideas.

Michael had a question about pg_switch_xlog, but he appeared to
reconsider that position after the subsequent discussion, which put us
back to essentially the same proposal that I started with, I believe. I
don't recall terribly much other discussion or concern about what roles
should be created or what should be included in each one, though I'd be
happy to hear your thoughts on what you'd like to see.

Honestly, my vote is for creating only those predefined roles that are
clearly endorsed by three people with different employers, which I
currently believe to be true of none of the proposed roles. It's not
even that I suspect you or anyone of ballot-stuffing; it's just that
people who work at different companies are likely to work with
different tools and those tools may have different requirements.

I'd love to have folks from other companies involved in these
discussions. I'll even reach out explicitly to seek their comment, as
I've done with other hackers at conferences, and try to get them to
voice their opinions here.

I
mean, people at 2ndQuadrant probably mostly use repmgr; people at
Crunchy probably like pgbackrest; people at OmniTI probably use
PITRtools; and EnterpriseDB employees are more likely than average to
be familiar with BART. If several of those people come together and
say they all agree that predefined role X is perfect for the needs of
all of their respective tools, I'd consider that a really good sign
that we've hit on something that is of general utility. Otherwise,
I'd just the authors of each tool specify the GRANT EXECUTE ON
FUNCTION lines necessary for their own tool and call it good. I think
that's almost as convenient and a lot more flexible.

If it didn't come with the other baggage that I've tried to explain, I'd
agree that having pg_dump support ACLs on catalog objects to be a
sensible and good thing, even if I might still argue that default roles
are better.

What really bothers me about this thread is that these predefined
roles are intended to be useful for third-party tools, but the people
who maintain those third-party tools have said basically nothing.

For my 2c, I believe that to be, by-and-large, because they don't want
to get their hopes up until they see something actually get committed.
Following long and deep threads such as these are quite a committment.

I
don't recall, for example, Dave Page weighing in on what pgAdmin
needs, or anybody commenting on to what degree any of these proposals
would meet the needs of Slony or pgBouncer or pgPool or any backup
tool (other than perhaps pgbackrest, which I assume your proposals
cater to) or any monitoring tool. Like, we've heard zip. Either
those people don't know this thread exists, or they can't understand
it, or they think it's so boring that they can't be bothered to write
in and say whether this is useful or not. I'd have a lot more
confidence that we are making a good decision if some of those people
would show up and say "I have reviewed this proposal and it looks {
great | terrible | mediocre } for $TOOL because $REASON".

We *have* heard complaints from people, multiple times on various lists,
that they'd like to set up check_postgres, Nagios, $MONITORINGTOOL, with
a role that *isn't* a superuser. I'll ask Greg S-M if he would have
time to weigh in on this though, check_postgres was specifically one of
the tools which I was looking at when considering the pg_monitor role.

I'm not sure about the references you use above to Slony or pgBouncer or
pgPool as those aren't backup tools, to my mind.. I would expect barman
and other backup tools to also use pg_start/stop_backup and
pg_switch_xlog. I'm not sure that there's a way to cater to one backup
role when it comes to how filesystem-level backups are handled in PG,
but perhaps I've missed something there that barman uses and which isn't
included currently.

Of course, my reviewing barman or other tools wouldn't have the same
support as Simon weighing in, so I'll try and pursue that avenue as
well.

Thanks!

Stephen

#155Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#153)
Re: Additional role attributes && superuser review

On Mon, Jan 4, 2016 at 4:56 PM, Stephen Frost <sfrost@snowman.net> wrote:

First, it's not really going to matter to users very much whether the
command to enable one of these features is a single GRANT command or a
short sequence of GRANT commands executed one after another. So even
if we don't have roles that bundle together multiple permissions, you
will still be able to do this; you just might need more than one
command. Probably, I'm guessing, not very many commands, but more
than one.

I disagree. I find that it does make a difference to users to be using
a well documented and clear approach over one which involves fiddling
with the individual pieces to get everything to work, and if a new
function comes along that is useful for backup users, that would have to
also be granted, even if it would clearly be useful to a backup role.

How is that a fair way to characterize the discussion here? Just
because you have to execute several SQL commands instead of one
doesn't turn a "well-documented and clear approach" into something
that involves "fiddling with individual pieces". Cutting and pasting
a sequence of 3 or 4 SQL commands into a psql window is not a lot
harder than copying and pasting a single one, and does not turn a good
approach into a shambles.

Second, I think it's really unlikely that you're going to maintain
perfect alignment between your predefined roles and the permissions
that third-party tools need over the course of multiple releases. I
think the chances of that working out are just about zero. Sure, you
can make the initial set of permissions granted to pg_backup match
exactly what pgbackrest needs, but it's a good bet that one of the six
or eight widely-used backup tools uses something that's not included
in that set.

There may be something I've missed with the proposed pg_backup role, but
I don't believe you're correct that there isn't a set of privileges
which all of those backup tools need and which could be provided through
the pg_backup role.

Well, there's certainly some set of privileges that will make them all
work. But it might include more than some of them want. If you done
any analysis of this, I have not seen it posted to this thread.

And even if not, it doesn't require very much
imagination to suppose that some tool, maybe pgbackrest or maybe
something else, that comes along over the next few releases will
require some extra permission. When that happens, will we include add
that permission to the pg_backup role, or not?

As I pointed out previously, we've already been doing this with the
replication role attribute and I don't recall any complaining about it.

1. This doesn't really seem like the same thing. You're introducing
something quite new here: these are not role attributes that apply
only to the role itself, but inheritable role attributes.

2. I believe that the discussion about what the replication role
should and should not allow involved considerably more people than
have discussed any of the specific roles you propose to add here.

3. It was clear from the outset that the replication role's basic
purpose was to be sufficient privilege for a streaming standby and no
more. The remit of these roles is a lot less clear, at least to me.

I wasn't suggesting that we give everyone the same privileges to some
default 'pgAdmin' role but rather that providing an abstraction of the
set of privileges possible against the catalog objects, into sets that
make sense together, is a useful simplification for users and that it'd
be a better approach than asking users to figure out what sets make
sense on their own.

I have no objection to that *in theory*. What's not clear to me is
that the way that you have broken it up actually meets the bona fide
needs of actual tools in a useful way.

Adding pg_dump dump and restore support for catalog ACLs implies that
we're supporting ACLs on all catalog objects- including tables.

Not to me it doesn't. I think we could support it just for functions,
and have it continue to be as weird as it is currently for other types
of objects until somebody gets around to straightening that out. If
we want to support it for more object types, great, but I don't think
that's a hard requirement.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#156Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#154)
Re: Additional role attributes && superuser review

On Mon, Jan 4, 2016 at 5:22 PM, Stephen Frost <sfrost@snowman.net> wrote:

So, is this another case where the support is all in off-list fora and
thus invisible, or can you point to specific on-list discussions where
it was supported, and to the opinions offered in support? I don't
really remember many opinions that were any more positive than "I
wouldn't be strongly opposed to this" or "If we're going to do this
then we ought to do it in X way". I'm happy to be corrected if I'm
misrepresenting the record, but I'd characterize the overall reaction
to this proposal as tepid: nobody hated it, but nobody really loved it
either, and a bunch of mild concerns were offered.

I agree that this has largely been the on-list reaction. To be fair,
it's been largely the off-list reaction also, which I've expressly
tried to seek out, as mentioned above. I'm not asking anyone to love
it, I'm not entirely convinced it's lovable myself, but I do feel it's
useful and worth making an effort for.

I think the question of whether the specific proposals on the table
are in fact useful is one that deserves more study. I am not
convinced of that. I believe something like this could be useful, but
I don't see a lot of evidence that the particular roles you're arguing
for actually are.

I'd love to have folks from other companies involved in these
discussions. I'll even reach out explicitly to seek their comment, as
I've done with other hackers at conferences, and try to get them to
voice their opinions here.

Great, thanks.

What really bothers me about this thread is that these predefined
roles are intended to be useful for third-party tools, but the people
who maintain those third-party tools have said basically nothing.

For my 2c, I believe that to be, by-and-large, because they don't want
to get their hopes up until they see something actually get committed.
Following long and deep threads such as these are quite a committment.

Yep.

I
don't recall, for example, Dave Page weighing in on what pgAdmin
needs, or anybody commenting on to what degree any of these proposals
would meet the needs of Slony or pgBouncer or pgPool or any backup
tool (other than perhaps pgbackrest, which I assume your proposals
cater to) or any monitoring tool. Like, we've heard zip. Either
those people don't know this thread exists, or they can't understand
it, or they think it's so boring that they can't be bothered to write
in and say whether this is useful or not. I'd have a lot more
confidence that we are making a good decision if some of those people
would show up and say "I have reviewed this proposal and it looks {
great | terrible | mediocre } for $TOOL because $REASON".

We *have* heard complaints from people, multiple times on various lists,
that they'd like to set up check_postgres, Nagios, $MONITORINGTOOL, with
a role that *isn't* a superuser.

True. But we should verify that this proposal actually meets those
needs, not just assume it does.

I'll ask Greg S-M if he would have
time to weigh in on this though, check_postgres was specifically one of
the tools which I was looking at when considering the pg_monitor role.

OK, that sounds like a good idea.

I'm not sure about the references you use above to Slony or pgBouncer or
pgPool as those aren't backup tools, to my mind.. I would expect barman
and other backup tools to also use pg_start/stop_backup and
pg_switch_xlog. I'm not sure that there's a way to cater to one backup
role when it comes to how filesystem-level backups are handled in PG,
but perhaps I've missed something there that barman uses and which isn't
included currently.

Oh, sure: they are not backup tools specifically. But anything that
might need elevated privileges deserves consideration here: what sort
of subdivision of the superuser role would make that need go away?

Of course, my reviewing barman or other tools wouldn't have the same
support as Simon weighing in, so I'll try and pursue that avenue as
well.

Cool.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#157Noah Misch
noah@leadboat.com
In reply to: Stephen Frost (#148)
Re: Additional role attributes && superuser review

On Mon, Jan 04, 2016 at 12:55:16PM -0500, Stephen Frost wrote:

* Noah Misch (noah@leadboat.com) wrote:

On Tue, Dec 29, 2015 at 08:35:50AM -0500, Stephen Frost wrote:

I'm approaching this largely from a 3rd-party application perspective.
There are two examples off-hand which I'm considering:

check_postgres
pgbackrest

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

check_postgres:

Most check_postgres sub-commands can be run without superuser
privileges by granting the pg_monitor role to the monitoring user:

GRANT pg_monitor TO monitor;

For information regarding the pg_monitor role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

Stop. Even if PostgreSQL introduces pg_monitor, check_postgres will do itself
a favor by not using it. The moment the two projects' sense of monitoring
privileges falls out of lockstep, benefits from using pg_monitor go negative.
check_postgres should instead furnish a script that creates a role holding
required privileges and/or SECURITY DEFINER helpers. If check_postgres starts
using an object, say pgstattuple, it will wish to use it in all versions.
Since PostgreSQL will change pg_monitor privileges in major releases only,
check_postgres would wait 6-18 months to use a privilege in just one of five
supported versions. If PostgreSQL hackers ever disagree with check_postgres
hackers about whether a privilege belongs in the pg_monitor role, then
check_postgres will wish it had never used pg_monitor. For a sample
controversy, some monitoring tool may well call pg_read_file, but that's
arguably too much power to be giving _every_ monitoring tool.

By the way, the pg_monitor role you were ready to commit does not cover
today's check_postgres needs. Among restricted objects, check_postgres uses
at least pg_ls_dir, pg_stats, pg_settings, and pg_stat_activity. Having said
that, it remains premature to design predefined roles. It would be a waste to
mobilize such a design effort with GRANT's limitation clouding the issue.

and, to reiterate what I said above, I'd rather have one abstraction for
these kinds of permissions instead of a mish-mash of instructions. The
difference I can imagine being between:

For backups and monitoring, you can use default roles:

GRANT pg_backup,pg_monitor to new_admin;

but for other regular privileges such as rotating logfiles, or sending
signals to other processes, you have to explicitly GRANT permissions:

GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO new_admin;
GRANT EXECUTE ON FUNCTION pg_signal_backend() TO new_admin;

I don't mind having those two ways to transfer privilege. If I have to settle
for one or the other, I pick the latter.

Further, I'm not convinced that adding support for dumping ACLs or, in
general, encouraging users to define their own ACLs on catalog objects
is a good idea. We certainly have no mechanism in place today for those
ACLs to be respected by SysCache and encouraging their use when we won't
actually respect them is likely to be confusing.

What's this problem with syscache? It sounds important.

CREATE TABLE and DROP TABLE aren't going to care one bit if you have
access to pg_class or not. The same goes for basically everything else.

If we really want to support ACLs on the catalog, we'd have to either
caveat that none of the internal lookups will respect them or revamp
SysCache and any other direct catalog access to do permission checks
first, which I don't think we really want to do.

Oh, that. Having internal lookups ignore the ACLs is more good than bad, and
users have little cause to expect something different. You don't need INSERT
on pg_attribute to add a column, so why expect lack of SELECT on pg_attribute
to prevent dropping one? I might document it like so:

While GRANT can modify the privileges of a system catalog table, that
affects only queries that address the catalog as an SQL table. Internal,
system access to the same underlying data will proceed normally. For
example, "REVOKE SELECT ON pg_proc FROM PUBLIC" does not preclude calling
functions or even preclude passing them to pg_get_functiondef. It does
block queries that name pg_proc in a FROM clause.

This entire discussion of privileges-on-catalog-objects should really
also consider the ongoing discussion about providing policies for the
catalog via RLS. If we start pg_dump'ing the ACLs of catalog objects
then we'd, presumably, also want to pg_dump out any policies defined
against catalog objects.

I would have no qualms supporting ACL changes while not supporting added
policies, indexes, triggers, rules, inheritance children, extra columns, etc.
Both Oracle and SQL Server invite system object GRANTs, but they don't invite
system object mutations generally.

nm

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#158Stephen Frost
sfrost@snowman.net
In reply to: Noah Misch (#157)
Re: Additional role attributes && superuser review

Robert, Noah,

I just wanted to start off by saying thank you for taking the time read
and comment with your thoughts on this concept. I was a bit frustrated
about it feeling rather late, but appreciate the comments which have
been made as they've certainly been constructive.

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 4, 2016 at 4:56 PM, Stephen Frost <sfrost@snowman.net> wrote:

First, it's not really going to matter to users very much whether the
command to enable one of these features is a single GRANT command or a
short sequence of GRANT commands executed one after another. So even
if we don't have roles that bundle together multiple permissions, you
will still be able to do this; you just might need more than one
command. Probably, I'm guessing, not very many commands, but more
than one.

I disagree. I find that it does make a difference to users to be using
a well documented and clear approach over one which involves fiddling
with the individual pieces to get everything to work, and if a new
function comes along that is useful for backup users, that would have to
also be granted, even if it would clearly be useful to a backup role.

How is that a fair way to characterize the discussion here? Just
because you have to execute several SQL commands instead of one
doesn't turn a "well-documented and clear approach" into something
that involves "fiddling with individual pieces". Cutting and pasting
a sequence of 3 or 4 SQL commands into a psql window is not a lot
harder than copying and pasting a single one, and does not turn a good
approach into a shambles.

I was looking at it from a perspective of what we have currently vs.
what the future state with default roles would be. That's not an
entirely fair characterization. If we supported ACLs on catalog objects
via pg_dump, then we could add documentation along the lines of "backups
generally need access to functions X, Y, Z, here's an example of how to
create such a role: blah, blah."

Of course, that documentation would likely have to be repeated in the
various backup tools, though it's possible they could tailor those, if
there was something different about their particular tool.

Second, I think it's really unlikely that you're going to maintain
perfect alignment between your predefined roles and the permissions
that third-party tools need over the course of multiple releases. I
think the chances of that working out are just about zero. Sure, you
can make the initial set of permissions granted to pg_backup match
exactly what pgbackrest needs, but it's a good bet that one of the six
or eight widely-used backup tools uses something that's not included
in that set.

There may be something I've missed with the proposed pg_backup role, but
I don't believe you're correct that there isn't a set of privileges
which all of those backup tools need and which could be provided through
the pg_backup role.

Well, there's certainly some set of privileges that will make them all
work. But it might include more than some of them want. If you done
any analysis of this, I have not seen it posted to this thread.

I can certainly work up a formal analysis and submit it for
consideration.

And even if not, it doesn't require very much
imagination to suppose that some tool, maybe pgbackrest or maybe
something else, that comes along over the next few releases will
require some extra permission. When that happens, will we include add
that permission to the pg_backup role, or not?

As I pointed out previously, we've already been doing this with the
replication role attribute and I don't recall any complaining about it.

1. This doesn't really seem like the same thing. You're introducing
something quite new here: these are not role attributes that apply
only to the role itself, but inheritable role attributes.

This approach started out by adding role attributes to handle these
kinds of rights, but in discussion with Tom and Magnus, iirc (no, I
don't have the specific links or threads, though I have asked Magnus to
take a look at this thread, as he has time), the idea of default roles
seemed better specifically because they would then be inheritable and
granting access could also be delegated.

2. I believe that the discussion about what the replication role
should and should not allow involved considerably more people than
have discussed any of the specific roles you propose to add here.

I didn't intend to dispute that, but...

3. It was clear from the outset that the replication role's basic
purpose was to be sufficient privilege for a streaming standby and no
more. The remit of these roles is a lot less clear, at least to me.

I've certainly intended the intention of these roles to be clear and
documented. The point I was trying to address above is that we
certainly appear fine to add additional privileges as new capabilities
are added to existing role attributes (the entire logical replication
system was added after the replication role attribute).

I wasn't suggesting that we give everyone the same privileges to some
default 'pgAdmin' role but rather that providing an abstraction of the
set of privileges possible against the catalog objects, into sets that
make sense together, is a useful simplification for users and that it'd
be a better approach than asking users to figure out what sets make
sense on their own.

I have no objection to that *in theory*. What's not clear to me is
that the way that you have broken it up actually meets the bona fide
needs of actual tools in a useful way.

That's certainly been my goal, but I've not been as clear as I should
have been regarding the analysis and background work which I have done.

Adding pg_dump dump and restore support for catalog ACLs implies that
we're supporting ACLs on all catalog objects- including tables.

Not to me it doesn't. I think we could support it just for functions,
and have it continue to be as weird as it is currently for other types
of objects until somebody gets around to straightening that out. If
we want to support it for more object types, great, but I don't think
that's a hard requirement.

Alright, that would certainly simplify things if we're talking about
only functions. The only concern which I have there is if there are any
non-function cases that we'll end up coming across, and I'm still a bit
nervous about the "old pg_dump / new database" restore concern, but
perhaps that's an acceptable issue.

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 4, 2016 at 5:22 PM, Stephen Frost <sfrost@snowman.net> wrote:

So, is this another case where the support is all in off-list fora and
thus invisible, or can you point to specific on-list discussions where
it was supported, and to the opinions offered in support? I don't
really remember many opinions that were any more positive than "I
wouldn't be strongly opposed to this" or "If we're going to do this
then we ought to do it in X way". I'm happy to be corrected if I'm
misrepresenting the record, but I'd characterize the overall reaction
to this proposal as tepid: nobody hated it, but nobody really loved it
either, and a bunch of mild concerns were offered.

I agree that this has largely been the on-list reaction. To be fair,
it's been largely the off-list reaction also, which I've expressly
tried to seek out, as mentioned above. I'm not asking anyone to love
it, I'm not entirely convinced it's lovable myself, but I do feel it's
useful and worth making an effort for.

I think the question of whether the specific proposals on the table
are in fact useful is one that deserves more study. I am not
convinced of that. I believe something like this could be useful, but
I don't see a lot of evidence that the particular roles you're arguing
for actually are.

Based on Noah's response (which I respond to below), we seem to still
be debating the whole concept of default roles. I'm happy to provide
detailed analysis if we're able to agree on the concept.

I'd love to have folks from other companies involved in these
discussions. I'll even reach out explicitly to seek their comment, as
I've done with other hackers at conferences, and try to get them to
voice their opinions here.

Great, thanks.

I've reached out to Magnus and Greg S-M so far. I've discussed this
quite a bit with David Steele already. I'll reach out to Marco
regarding barman.

I'm not sure about the references you use above to Slony or pgBouncer or
pgPool as those aren't backup tools, to my mind.. I would expect barman
and other backup tools to also use pg_start/stop_backup and
pg_switch_xlog. I'm not sure that there's a way to cater to one backup
role when it comes to how filesystem-level backups are handled in PG,
but perhaps I've missed something there that barman uses and which isn't
included currently.

Oh, sure: they are not backup tools specifically. But anything that
might need elevated privileges deserves consideration here: what sort
of subdivision of the superuser role would make that need go away?

The general approach which I've been using for the default roles is that
they should grant rights which aren't able to be used to trivially make
oneself a superuser.

* Noah Misch (noah@leadboat.com) wrote:

On Mon, Jan 04, 2016 at 12:55:16PM -0500, Stephen Frost wrote:

* Noah Misch (noah@leadboat.com) wrote:

On Tue, Dec 29, 2015 at 08:35:50AM -0500, Stephen Frost wrote:

I'm approaching this largely from a 3rd-party application perspective.
There are two examples off-hand which I'm considering:

check_postgres
pgbackrest

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

check_postgres:

Most check_postgres sub-commands can be run without superuser
privileges by granting the pg_monitor role to the monitoring user:

GRANT pg_monitor TO monitor;

For information regarding the pg_monitor role, see:
http://www.postgresql.org/docs/current/static/roles/database-roles.html

Stop. Even if PostgreSQL introduces pg_monitor, check_postgres will do itself
a favor by not using it. The moment the two projects' sense of monitoring
privileges falls out of lockstep, benefits from using pg_monitor go negative.
check_postgres should instead furnish a script that creates a role holding
required privileges and/or SECURITY DEFINER helpers. If check_postgres starts
using an object, say pgstattuple, it will wish to use it in all versions.
Since PostgreSQL will change pg_monitor privileges in major releases only,
check_postgres would wait 6-18 months to use a privilege in just one of five
supported versions. If PostgreSQL hackers ever disagree with check_postgres
hackers about whether a privilege belongs in the pg_monitor role, then
check_postgres will wish it had never used pg_monitor. For a sample
controversy, some monitoring tool may well call pg_read_file, but that's
arguably too much power to be giving _every_ monitoring tool.

Greg and I have chatted previously, and recently again yesterday, about
the idea of providing SECURITY DEFINER functions and neither of us care
for that approach. A role which is granted the necessary rights might
be alright, though I would still contend that a default role is better
and I don't particularly agree with the slow turn-around time-
check_postgres is quite mature and does not change very much any more.

By the way, the pg_monitor role you were ready to commit does not cover
today's check_postgres needs. Among restricted objects, check_postgres uses
at least pg_ls_dir, pg_stats, pg_settings, and pg_stat_activity. Having said
that, it remains premature to design predefined roles. It would be a waste to
mobilize such a design effort with GRANT's limitation clouding the issue.

I reviewed check_postgres and wished to avoid functions that were overly
broad for what check_postgres actually needed and those which would
allow the monitoring role to be able to become superuser easily. That
is where the set of permissions actually granted to pg_monitor came
from. The checks of things like "number of WAL segments" are cases
where I'd rather we provide an explicit function that only provided that
information and then allow pg_monitor to use it rather than simply grant
pg_ls_dir access. I don't think that waiting until 9.7 or 10.0 for that
would be any problem at all for check_postgres, which is why I had
wished to get the basic default roles concept completed before moving on
to add such additional functions.

To put it another way, I'd rather *not* tell check_postgres users "sure,
just grant access to pg_ls_dir to the monitoring user" as that really
grants more access than the check_postgres check requires.

and, to reiterate what I said above, I'd rather have one abstraction for
these kinds of permissions instead of a mish-mash of instructions. The
difference I can imagine being between:

For backups and monitoring, you can use default roles:

GRANT pg_backup,pg_monitor to new_admin;

but for other regular privileges such as rotating logfiles, or sending
signals to other processes, you have to explicitly GRANT permissions:

GRANT EXECUTE ON FUNCTION pg_rotate_logfile() TO new_admin;
GRANT EXECUTE ON FUNCTION pg_signal_backend() TO new_admin;

I don't mind having those two ways to transfer privilege. If I have to settle
for one or the other, I pick the latter.

Of course, if we did provide both, then any difference might be able to
be addressed as a delta against what is provided by the default role.

Further, I'm not convinced that adding support for dumping ACLs or, in
general, encouraging users to define their own ACLs on catalog objects
is a good idea. We certainly have no mechanism in place today for those
ACLs to be respected by SysCache and encouraging their use when we won't
actually respect them is likely to be confusing.

What's this problem with syscache? It sounds important.

CREATE TABLE and DROP TABLE aren't going to care one bit if you have
access to pg_class or not. The same goes for basically everything else.

If we really want to support ACLs on the catalog, we'd have to either
caveat that none of the internal lookups will respect them or revamp
SysCache and any other direct catalog access to do permission checks
first, which I don't think we really want to do.

Oh, that. Having internal lookups ignore the ACLs is more good than bad, and
users have little cause to expect something different. You don't need INSERT
on pg_attribute to add a column, so why expect lack of SELECT on pg_attribute
to prevent dropping one? I might document it like so:

While GRANT can modify the privileges of a system catalog table, that
affects only queries that address the catalog as an SQL table. Internal,
system access to the same underlying data will proceed normally. For
example, "REVOKE SELECT ON pg_proc FROM PUBLIC" does not preclude calling
functions or even preclude passing them to pg_get_functiondef. It does
block queries that name pg_proc in a FROM clause.

That's a particularly good example, as it is frequently requested that
prosrc be hidden. Though what is really desired is for users to be able
to see all functions they can call, but only see prosrc for the
functions they own. That kind of policy is where I believe we should be
taking CREATE POLICY, in the future. That's discussion for another
thread though, by and large.

This entire discussion of privileges-on-catalog-objects should really
also consider the ongoing discussion about providing policies for the
catalog via RLS. If we start pg_dump'ing the ACLs of catalog objects
then we'd, presumably, also want to pg_dump out any policies defined
against catalog objects.

I would have no qualms supporting ACL changes while not supporting added
policies, indexes, triggers, rules, inheritance children, extra columns, etc.

Alright.

Both Oracle and SQL Server invite system object GRANTs, but they don't invite
system object mutations generally.

Interesting. I hadn't assessed how system object GRANTs in those
systems were handled, so that's good to know.

Thanks!

Stephen

#159Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#158)
Re: Additional role attributes && superuser review

On Wed, Jan 6, 2016 at 11:13 AM, Stephen Frost <sfrost@snowman.net> wrote:

I just wanted to start off by saying thank you for taking the time read
and comment with your thoughts on this concept. I was a bit frustrated
about it feeling rather late, but appreciate the comments which have
been made as they've certainly been constructive.

Thanks.

Well, there's certainly some set of privileges that will make them all
work. But it might include more than some of them want. If you done
any analysis of this, I have not seen it posted to this thread.

I can certainly work up a formal analysis and submit it for
consideration.

I would be in favor of that.

1. This doesn't really seem like the same thing. You're introducing
something quite new here: these are not role attributes that apply
only to the role itself, but inheritable role attributes.

This approach started out by adding role attributes to handle these
kinds of rights, but in discussion with Tom and Magnus, iirc (no, I
don't have the specific links or threads, though I have asked Magnus to
take a look at this thread, as he has time), the idea of default roles
seemed better specifically because they would then be inheritable and
granting access could also be delegated.

I agree that predefined roles are better than additional role
attributes. I don't agree that predefined roles are better than GRANT
EXECUTE ON FUNCTION pg_catalog.blah(). I think full support for GRANT
EXECUTE ON FUNCTION pg_catalog.blah() is undeniably useful and will be
a very clear improvement over what we have now. I think predefined
roles are a reasonable thing for somebody to want, but I don't think
it's nearly as clear-cut, and I'm very much unconvinced that we know
that the ones you're proposing are the right ones.

3. It was clear from the outset that the replication role's basic
purpose was to be sufficient privilege for a streaming standby and no
more. The remit of these roles is a lot less clear, at least to me.

I've certainly intended the intention of these roles to be clear and
documented. The point I was trying to address above is that we
certainly appear fine to add additional privileges as new capabilities
are added to existing role attributes (the entire logical replication
system was added after the replication role attribute).

Mmmph. I think we got lucky there as much as anything. replication
already allows sufficient privilege to extract all data in the
database, so allowing it to also request a logical change stream isn't
really a privilege escalation. That's not going to be true for what
you are proposing here.

Adding pg_dump dump and restore support for catalog ACLs implies that
we're supporting ACLs on all catalog objects- including tables.

Not to me it doesn't. I think we could support it just for functions,
and have it continue to be as weird as it is currently for other types
of objects until somebody gets around to straightening that out. If
we want to support it for more object types, great, but I don't think
that's a hard requirement.

Alright, that would certainly simplify things if we're talking about
only functions. The only concern which I have there is if there are any
non-function cases that we'll end up coming across, and I'm still a bit
nervous about the "old pg_dump / new database" restore concern, but
perhaps that's an acceptable issue.

I wouldn't worry about that a bit. We recommend that users always
pg_dump using the newest version of pg_dump, even if the dumped server
is older. That advice will be entirely satisfactory in this case
also.

Based on Noah's response (which I respond to below), we seem to still
be debating the whole concept of default roles. I'm happy to provide
detailed analysis if we're able to agree on the concept.

I think you need to provide detailed analysis SO THAT we can agree on
the concept.

Oh, sure: they are not backup tools specifically. But anything that
might need elevated privileges deserves consideration here: what sort
of subdivision of the superuser role would make that need go away?

The general approach which I've been using for the default roles is that
they should grant rights which aren't able to be used to trivially make
oneself a superuser.

That's a good principle, but not a sufficient guide.

Stop. Even if PostgreSQL introduces pg_monitor, check_postgres will do itself
a favor by not using it. The moment the two projects' sense of monitoring
privileges falls out of lockstep, benefits from using pg_monitor go negative.
check_postgres should instead furnish a script that creates a role holding
required privileges and/or SECURITY DEFINER helpers. If check_postgres starts
using an object, say pgstattuple, it will wish to use it in all versions.
Since PostgreSQL will change pg_monitor privileges in major releases only,
check_postgres would wait 6-18 months to use a privilege in just one of five
supported versions. If PostgreSQL hackers ever disagree with check_postgres
hackers about whether a privilege belongs in the pg_monitor role, then
check_postgres will wish it had never used pg_monitor. For a sample
controversy, some monitoring tool may well call pg_read_file, but that's
arguably too much power to be giving _every_ monitoring tool.

Greg and I have chatted previously, and recently again yesterday, about
the idea of providing SECURITY DEFINER functions and neither of us care
for that approach. A role which is granted the necessary rights might
be alright, though I would still contend that a default role is better
and I don't particularly agree with the slow turn-around time-
check_postgres is quite mature and does not change very much any more.

I am pretty much entirely in agreement with Noah's analysis here and I
think he put it better than I ever could have done. The rate of
change of check_postgres is utterly irrelevant. We are not going to
have a role called pg_check_postgres built into the database. We
would have, at most, a role called pg_monitor, which would be expected
to be generic across all monitoring tools. But all monitoring tools
will not have the same permissions needs. And they will certainly not
all develop equally quickly. EnterpriseDB has an entire team of
people working on our monitoring tool (PEM). Maybe it's not growing
any new features that require additional permissions at this point,
but that can change as fast as somebody can rewrite the roadmap.
Somebody could offer Greg a million dollars tomorrow to add a whole
bunch of new features to check_postgres.pl, and he might agree to take
on the work.

The point is that with the GRANT EXECUTE ON FUNCTION proposal, authors
of monitoring tools enjoy various really noteworthy advantages. They
can have monitoring roles which have *exactly* the privileges that
their tool needs, not whatever set of permissions (larger or smaller)
the core project has decide the pg_monitor role should have. They can
have optional features requiring extra permissions and those extra
permissions can be granted in precisely those shops where those extra
features are in use. They can deploy a new versions of their
monitoring tool that requires an extra privilege on an existing
PostgreSQL release without requiring any core modifications, which
shaves years of time off the deployment schedule and avoids
contentious arguments with the lovable folks who populate this mailing
list. That sounds *terrific* to me compared to the alternative you
are proposing.

I don't think that waiting until 9.7 or 10.0 for that
would be any problem at all for check_postgres,

I think it's nuts to design a solution on the theory that people won't
mind waiting 2 or 3 years to be able to enable a new feature in their
product. Maybe Greg won't mind personally, but if users don't mind
not having the feature for 2 or 3 years, it wasn't a very important
feature in the first place. No software product I've ever worked on
personally could afford to wait an extra 2 or 3 years to deploy a
feature that was truly needed, and most managers I've worked for would
pop a gasket.

To put it another way, I'd rather *not* tell check_postgres users "sure,
just grant access to pg_ls_dir to the monitoring user" as that really
grants more access than the check_postgres check requires.

Sure, that's clearly right, but it doesn't making the pg_monitor
approach better than individual function grants. If you provide a
better alternative there, people can start granting access to that
instead, and in the meantime they can decide whether or not they want
to take the risk of granting access to pg_ls_dir().

Of course, if we did provide both, then any difference might be able to
be addressed as a delta against what is provided by the default role.

You can grant extra rights, but you can't revoke some of them away AFAIK.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#160Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#148)
Re: Additional role attributes && superuser review

On Mon, Jan 4, 2016 at 12:55:16PM -0500, Stephen Frost wrote:

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

...

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

------

Just to clarify, the 'postgres' OS user group cannot read the data
directory, e.g.

drwx------ 19 postgres staff 4096 Jan 17 12:19 data/
^^^group

I assume we don't want to change that.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#161Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#160)
Re: Additional role attributes && superuser review

* Bruce Momjian (bruce@momjian.us) wrote:

On Mon, Jan 4, 2016 at 12:55:16PM -0500, Stephen Frost wrote:

I'd like to be able to include, in both of those, a simple set of
instructions for granting the necessary rights to the user who is
running those processes. A set of rights which an administrator can go
look up and easily read and understand the result of those grants. For
example:

...

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

------

Just to clarify, the 'postgres' OS user group cannot read the data
directory, e.g.

drwx------ 19 postgres staff 4096 Jan 17 12:19 data/
^^^group

I assume we don't want to change that.

This is going to be distribution dependent, unfortunately. On
Debian-based distributions, the group is 'postgres' and it'd be
perfectly reasonable to allow that group to read the data directory.

I don't recall offhand if that means we'd have to make changes to allow
that, but, for my 2c, I don't see why we wouldn't allow it to be an
option.

Thanks!

Stephen

#162Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#161)
Re: Additional role attributes && superuser review

On Sun, Jan 17, 2016 at 01:49:19PM -0500, Stephen Frost wrote:

* Bruce Momjian (bruce@momjian.us) wrote:

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

------

Just to clarify, the 'postgres' OS user group cannot read the data
directory, e.g.

drwx------ 19 postgres staff 4096 Jan 17 12:19 data/
^^^group

I assume we don't want to change that.

This is going to be distribution dependent, unfortunately. On
Debian-based distributions, the group is 'postgres' and it'd be
perfectly reasonable to allow that group to read the data directory.

Well, while the group name would be OS-dependent, the lack of any group
permisions in not OS-dependent and is forced by initdb:

umask(S_IRWXG | S_IRWXO);

create_data_directory();

I don't recall offhand if that means we'd have to make changes to allow
that, but, for my 2c, I don't see why we wouldn't allow it to be an
option.

OK, that would be an initdb change then.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#163Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#162)
Re: Additional role attributes && superuser review

* Bruce Momjian (bruce@momjian.us) wrote:

On Sun, Jan 17, 2016 at 01:49:19PM -0500, Stephen Frost wrote:

* Bruce Momjian (bruce@momjian.us) wrote:

pgbackrest:

To run pgbackrest as a non-superuser and not the 'postgres' system
user, grant the pg_backup role to the backrest user and ensure the
backrest system user has read access to the database files (eg: by
having the system user be a member of the 'postgres' group):

------

Just to clarify, the 'postgres' OS user group cannot read the data
directory, e.g.

drwx------ 19 postgres staff 4096 Jan 17 12:19 data/
^^^group

I assume we don't want to change that.

This is going to be distribution dependent, unfortunately. On
Debian-based distributions, the group is 'postgres' and it'd be
perfectly reasonable to allow that group to read the data directory.

Well, while the group name would be OS-dependent, the lack of any group
permisions in not OS-dependent and is forced by initdb:

umask(S_IRWXG | S_IRWXO);

create_data_directory();

Right, we also check in the backend on startup for certain permissions.
I don't recall offhand if that's forced to 700 or if we allow 750.

I don't recall offhand if that means we'd have to make changes to allow
that, but, for my 2c, I don't see why we wouldn't allow it to be an
option.

OK, that would be an initdb change then.

It would need to be optional, so distributions and users could choose
which makes sense for their systems.

Thanks!

Stephen

#164Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#159)
Re: Additional role attributes && superuser review

On Wed, Jan 6, 2016 at 12:29:14PM -0500, Robert Haas wrote:

The point is that with the GRANT EXECUTE ON FUNCTION proposal, authors
of monitoring tools enjoy various really noteworthy advantages. They
can have monitoring roles which have *exactly* the privileges that
their tool needs, not whatever set of permissions (larger or smaller)
the core project has decide the pg_monitor role should have. They can
have optional features requiring extra permissions and those extra
permissions can be granted in precisely those shops where those extra
features are in use. They can deploy a new versions of their
monitoring tool that requires an extra privilege on an existing
PostgreSQL release without requiring any core modifications, which
shaves years of time off the deployment schedule and avoids
contentious arguments with the lovable folks who populate this mailing
list. That sounds *terrific* to me compared to the alternative you
are proposing.

I assume backup tools would either document the functions they want
access to via SQL commands, or supply a script. I assume they would
create a non-login role (group) with the desired permissions, and then
have users inherit from that. They would also need to be able to allow
upgrades where they would (conditionally?) add the role and then
add/revoke permissions as needed, e.g. they might not need all
permissions they needed in a previous release, or they might need new
ones.

That all seems very straight-forward to me.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#165Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#164)
Re: Additional role attributes && superuser review

* Bruce Momjian (bruce@momjian.us) wrote:

On Wed, Jan 6, 2016 at 12:29:14PM -0500, Robert Haas wrote:

The point is that with the GRANT EXECUTE ON FUNCTION proposal, authors
of monitoring tools enjoy various really noteworthy advantages. They
can have monitoring roles which have *exactly* the privileges that
their tool needs, not whatever set of permissions (larger or smaller)
the core project has decide the pg_monitor role should have. They can
have optional features requiring extra permissions and those extra
permissions can be granted in precisely those shops where those extra
features are in use. They can deploy a new versions of their
monitoring tool that requires an extra privilege on an existing
PostgreSQL release without requiring any core modifications, which
shaves years of time off the deployment schedule and avoids
contentious arguments with the lovable folks who populate this mailing
list. That sounds *terrific* to me compared to the alternative you
are proposing.

I assume backup tools would either document the functions they want
access to via SQL commands, or supply a script. I assume they would
create a non-login role (group) with the desired permissions, and then
have users inherit from that. They would also need to be able to allow
upgrades where they would (conditionally?) add the role and then
add/revoke permissions as needed, e.g. they might not need all
permissions they needed in a previous release, or they might need new
ones.

That all seems very straight-forward to me.

I'm not against that idea, though I continue to feel that there are
common sets of privileges which backup tools could leverage.

The other issue that I'm running into, again, while considering how to
move back to ACL-based permissions for these objects is that we can't
grant out the actual permissions which currently exist. That means we
either need to break backwards compatibility, which would be pretty
ugly, in my view, or come up with new functions and then users will have
to know which functions to use when.

As I don't think we really want to break backwards compatibility or
remove existing functionality, the only approach which is going to make
sense is to add additional functions in some cases. In particular, we
will need alternate versions of pg_terminate_backend and
pg_cancel_backend. One thought I had was to make that
'pg_signal_backend', but that sounds like we'd allow any signal sent by
a user with that right, which seems a bit much to me...

Perhaps that's what I'll do though, barring other suggestions.

Thanks!

Stephen

#166Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#163)
Re: Additional role attributes && superuser review

On Sun, Jan 17, 2016 at 01:57:22PM -0500, Stephen Frost wrote:

Right, we also check in the backend on startup for certain permissions.
I don't recall offhand if that's forced to 700 or if we allow 750.

I don't recall offhand if that means we'd have to make changes to allow
that, but, for my 2c, I don't see why we wouldn't allow it to be an
option.

OK, that would be an initdb change then.

It would need to be optional, so distributions and users could choose
which makes sense for their systems.

While the group owner of the directory is a distributions question, the
permissions are usually a backup-method-specific requirement. I can see
us creating an SQL function that opens up group permissions on the data
directory for specific backup tools that need it, then granting
permissions on that function to the backup role. This is another
example where different backup tools need different permissions.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#167Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#165)
Re: Additional role attributes && superuser review

On Sun, Jan 17, 2016 at 06:58:25PM -0500, Stephen Frost wrote:

I'm not against that idea, though I continue to feel that there are
common sets of privileges which backup tools could leverage.

The other issue that I'm running into, again, while considering how to
move back to ACL-based permissions for these objects is that we can't
grant out the actual permissions which currently exist. That means we

Is that because many of them are complex, e.g. you can kill only your
own sessions?

either need to break backwards compatibility, which would be pretty
ugly, in my view, or come up with new functions and then users will have
to know which functions to use when.

As I don't think we really want to break backwards compatibility or
remove existing functionality, the only approach which is going to make
sense is to add additional functions in some cases. In particular, we
will need alternate versions of pg_terminate_backend and
pg_cancel_backend. One thought I had was to make that

Like these? Could we define own-user-type rights?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#168Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#166)
Re: Additional role attributes && superuser review

Bruce,

* Bruce Momjian (bruce@momjian.us) wrote:

On Sun, Jan 17, 2016 at 01:57:22PM -0500, Stephen Frost wrote:

Right, we also check in the backend on startup for certain permissions.
I don't recall offhand if that's forced to 700 or if we allow 750.

I don't recall offhand if that means we'd have to make changes to allow
that, but, for my 2c, I don't see why we wouldn't allow it to be an
option.

OK, that would be an initdb change then.

It would need to be optional, so distributions and users could choose
which makes sense for their systems.

While the group owner of the directory is a distributions question, the
permissions are usually a backup-method-specific requirement. I can see
us creating an SQL function that opens up group permissions on the data
directory for specific backup tools that need it, then granting
permissions on that function to the backup role. This is another
example where different backup tools need different permissions.

I don't believe we can really consider group ownership and group
permissions independently. They really go hand-in-hand. On
RedHat-based system, where the group is set as 'staff', you probably
don't want group permissions to be allowed. On Debian-based systems,
where there is a dedicated 'postgres' group, group permissions are fine
to allow.

Group ownership and permissions aren't a backup-method-specific
requirement either, in my view. I'm happy to chat with Marco (who has
said he would be weighing in on this thread when he is able to)
regarding barman, and whomever would be appropriate for BART (perhaps
you could let me know..?), but if it's possible to do a backup without
being a superuser and with only read access to the data directory, I
would expect every backup soltuion to view that as a feature which they
want to support, as there are environments which will find it desirable,
at a minimum, and even some which will require it.

Lastly, I'm pretty sure this would have to be a postgresql.conf option
as we check the permissions on the data directory on startup. I don't
see how having an SQL function would work there as I certainly wouldn't
want to have the permissions changing on a running system. That strikes
me as being risky without any real benefit. Either it's safe and
acceptable to allow those rights to the group, or it isn't. A temproary
grant really isn't helping with anything that I can see, surely there
are numerous ways to exploit such a time-based grant.

Thanks!

Stephen

#169Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#167)
Re: Additional role attributes && superuser review

* Bruce Momjian (bruce@momjian.us) wrote:

On Sun, Jan 17, 2016 at 06:58:25PM -0500, Stephen Frost wrote:

I'm not against that idea, though I continue to feel that there are
common sets of privileges which backup tools could leverage.

The other issue that I'm running into, again, while considering how to
move back to ACL-based permissions for these objects is that we can't
grant out the actual permissions which currently exist. That means we

Is that because many of them are complex, e.g. you can kill only your
own sessions?

Right.

either need to break backwards compatibility, which would be pretty
ugly, in my view, or come up with new functions and then users will have
to know which functions to use when.

As I don't think we really want to break backwards compatibility or
remove existing functionality, the only approach which is going to make
sense is to add additional functions in some cases. In particular, we
will need alternate versions of pg_terminate_backend and
pg_cancel_backend. One thought I had was to make that

Like these? Could we define own-user-type rights?

Interesting idea but I don't really see that being general enough that
we would want to burn a GRANT bit for it...

Thanks!

Stephen

#170Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#168)
Re: Additional role attributes && superuser review

On Sun, Jan 17, 2016 at 09:10:23PM -0500, Stephen Frost wrote:

While the group owner of the directory is a distributions question, the
permissions are usually a backup-method-specific requirement. I can see
us creating an SQL function that opens up group permissions on the data
directory for specific backup tools that need it, then granting
permissions on that function to the backup role. This is another
example where different backup tools need different permissions.

I don't believe we can really consider group ownership and group
permissions independently. They really go hand-in-hand. On
RedHat-based system, where the group is set as 'staff', you probably
don't want group permissions to be allowed. On Debian-based systems,
where there is a dedicated 'postgres' group, group permissions are fine
to allow.

Yes, I can see that as problematic. Seems it would have to be something
done by the administrator from the command-line.

Group ownership and permissions aren't a backup-method-specific
requirement either, in my view. I'm happy to chat with Marco (who has
said he would be weighing in on this thread when he is able to)
regarding barman, and whomever would be appropriate for BART (perhaps
you could let me know..?), but if it's possible to do a backup without
being a superuser and with only read access to the data directory, I
would expect every backup soltuion to view that as a feature which they
want to support, as there are environments which will find it desirable,
at a minimum, and even some which will require it.

pg_dump doesn't need to read the PGDATA directory, and I thought this
permission was to be used by pg_dump users as well.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#171Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#170)
Re: Additional role attributes && superuser review

* Bruce Momjian (bruce@momjian.us) wrote:

On Sun, Jan 17, 2016 at 09:10:23PM -0500, Stephen Frost wrote:

While the group owner of the directory is a distributions question, the
permissions are usually a backup-method-specific requirement. I can see
us creating an SQL function that opens up group permissions on the data
directory for specific backup tools that need it, then granting
permissions on that function to the backup role. This is another
example where different backup tools need different permissions.

I don't believe we can really consider group ownership and group
permissions independently. They really go hand-in-hand. On
RedHat-based system, where the group is set as 'staff', you probably
don't want group permissions to be allowed. On Debian-based systems,
where there is a dedicated 'postgres' group, group permissions are fine
to allow.

Yes, I can see that as problematic. Seems it would have to be something
done by the administrator from the command-line.

initdb on both RedHat and Debian-based systems is run, generally
speaking, from the packaging scripts. They would be able to pass the
correct options to initdb (or PG itself, if we decide that's
necessary..).

Group ownership and permissions aren't a backup-method-specific
requirement either, in my view. I'm happy to chat with Marco (who has
said he would be weighing in on this thread when he is able to)
regarding barman, and whomever would be appropriate for BART (perhaps
you could let me know..?), but if it's possible to do a backup without
being a superuser and with only read access to the data directory, I
would expect every backup soltuion to view that as a feature which they
want to support, as there are environments which will find it desirable,
at a minimum, and even some which will require it.

pg_dump doesn't need to read the PGDATA directory, and I thought this
permission was to be used by pg_dump users as well.

No. That has been a source of confusion, though I'm not quite sure how
or why, beyond the general assumption that anything 'backup' must
include 'pg_dump' (I don't generally consider that to be the case,
myself, but it seems others do...).

This is only for file-based backups.

Thanks!

Stephen

#172Bruce Momjian
bruce@momjian.us
In reply to: Stephen Frost (#171)
Re: Additional role attributes && superuser review

On Sun, Jan 17, 2016 at 09:23:14PM -0500, Stephen Frost wrote:

Group ownership and permissions aren't a backup-method-specific
requirement either, in my view. I'm happy to chat with Marco (who has
said he would be weighing in on this thread when he is able to)
regarding barman, and whomever would be appropriate for BART (perhaps
you could let me know..?), but if it's possible to do a backup without
being a superuser and with only read access to the data directory, I
would expect every backup soltuion to view that as a feature which they
want to support, as there are environments which will find it desirable,
at a minimum, and even some which will require it.

pg_dump doesn't need to read the PGDATA directory, and I thought this
permission was to be used by pg_dump users as well.

No. That has been a source of confusion, though I'm not quite sure how
or why, beyond the general assumption that anything 'backup' must
include 'pg_dump' (I don't generally consider that to be the case,
myself, but it seems others do...).

I think the source of that is that many people have asked for
backup-only uses, and I thought running pg_dump or pg_dumpall was one of
those cases.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#173Stephen Frost
sfrost@snowman.net
In reply to: Bruce Momjian (#172)
Re: Additional role attributes && superuser review

* Bruce Momjian (bruce@momjian.us) wrote:

On Sun, Jan 17, 2016 at 09:23:14PM -0500, Stephen Frost wrote:

Group ownership and permissions aren't a backup-method-specific
requirement either, in my view. I'm happy to chat with Marco (who has
said he would be weighing in on this thread when he is able to)
regarding barman, and whomever would be appropriate for BART (perhaps
you could let me know..?), but if it's possible to do a backup without
being a superuser and with only read access to the data directory, I
would expect every backup soltuion to view that as a feature which they
want to support, as there are environments which will find it desirable,
at a minimum, and even some which will require it.

pg_dump doesn't need to read the PGDATA directory, and I thought this
permission was to be used by pg_dump users as well.

No. That has been a source of confusion, though I'm not quite sure how
or why, beyond the general assumption that anything 'backup' must
include 'pg_dump' (I don't generally consider that to be the case,
myself, but it seems others do...).

I think the source of that is that many people have asked for
backup-only uses, and I thought running pg_dump or pg_dumpall was one of
those cases.

I'd want that to be a seperate permission which would really be
something along the lines of "allowed to read everything through a DB
connection", which is what pg_dump/pg_dumpall need.

Thanks!

Stephen

#174Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#165)
Re: Additional role attributes && superuser review

On Sun, Jan 17, 2016 at 6:58 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'm not against that idea, though I continue to feel that there are
common sets of privileges which backup tools could leverage.

The other issue that I'm running into, again, while considering how to
move back to ACL-based permissions for these objects is that we can't
grant out the actual permissions which currently exist. That means we
either need to break backwards compatibility, which would be pretty
ugly, in my view, or come up with new functions and then users will have
to know which functions to use when.

As I don't think we really want to break backwards compatibility or
remove existing functionality, the only approach which is going to make
sense is to add additional functions in some cases. In particular, we
will need alternate versions of pg_terminate_backend and
pg_cancel_backend. One thought I had was to make that
'pg_signal_backend', but that sounds like we'd allow any signal sent by
a user with that right, which seems a bit much to me...

So, this seems like a case where a built-in role would be
well-justified. I don't really believe in built-in roles as a way of
bundling related permissions; I know you do, but I don't. I'd rather
see the individual function permissions granted individually. But
here you are talking about a variable level of access to the same
function, depending on role. And for that it seems to me that a
built-in role has a lot more to recommend it in that case than it does
in the other. If you have been granted pg_whack, you can signal any
process on the system; otherwise just your own. Those checks are
internal to pg_terminate_backend/pg_cancel_backend so GRANT is not a
substitute.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#175David Steele
david@pgmasters.net
In reply to: Stephen Frost (#168)
Re: Additional role attributes && superuser review

On 1/17/16 9:10 PM, Stephen Frost wrote:

but if it's possible to do a backup without
being a superuser and with only read access to the data directory, I
would expect every backup soltuion to view that as a feature which they
want to support, as there are environments which will find it desirable,
at a minimum, and even some which will require it.

This would certainly be a desirable feature for pgBackRest. The fewer
processes running with the ability to alter the cluster the better.
Even if they are well-tested it's still one less thing to worry about.

--
-David
david@pgmasters.net

#176Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#174)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Sun, Jan 17, 2016 at 6:58 PM, Stephen Frost <sfrost@snowman.net> wrote:

I'm not against that idea, though I continue to feel that there are
common sets of privileges which backup tools could leverage.

The other issue that I'm running into, again, while considering how to
move back to ACL-based permissions for these objects is that we can't
grant out the actual permissions which currently exist. That means we
either need to break backwards compatibility, which would be pretty
ugly, in my view, or come up with new functions and then users will have
to know which functions to use when.

As I don't think we really want to break backwards compatibility or
remove existing functionality, the only approach which is going to make
sense is to add additional functions in some cases. In particular, we
will need alternate versions of pg_terminate_backend and
pg_cancel_backend. One thought I had was to make that
'pg_signal_backend', but that sounds like we'd allow any signal sent by
a user with that right, which seems a bit much to me...

So, this seems like a case where a built-in role would be
well-justified. I don't really believe in built-in roles as a way of
bundling related permissions; I know you do, but I don't. I'd rather
see the individual function permissions granted individually. But
here you are talking about a variable level of access to the same
function, depending on role. And for that it seems to me that a
built-in role has a lot more to recommend it in that case than it does
in the other. If you have been granted pg_whack, you can signal any
process on the system; otherwise just your own. Those checks are
internal to pg_terminate_backend/pg_cancel_backend so GRANT is not a
substitute.

If we extend this into the future then it seems to potentially fall
afoul of Noah's concern that we're going to end up with two different
ways of saying GRANT EXECUTE X ON Y. Consider the more complicated case
of pg_stat_activity, where values are shown or hidden based on who the
current role is. The policy system only supports whole-row, today, but
the question has already come up, both on the lists and off, of support
for hiding individual column values for certain rows, exactly as we do
today for pg_stat_activity. Once we reach that point, we'll have a way
to GRANT out these rights and a default role which just has them.

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

What got me thinking along these lines was a question posed by Bruce
(Bruce, feel free to chime in if I've misunderstood) to me at SCALE
where we were chatting a bit about this, which was- how could we extend
GRANT to support the permissions that we actually wish
pg_terminate_backend() and pg_cancel_backend() to have, instead of using
a default role? I didn't think too much on it at the time as it strikes
me as a pretty narrow use-case and requiring quite a bit of complexity
to get right, but there again, I'd certainly view a system where the
user is allowed to have pg_start_backup() rights but not
pg_stop_backup() as being quite a small use-case also, yet that's the
direction we're largely going in with this discussion.

Thanks!

Stephen

#177Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#176)
Re: Additional role attributes && superuser review

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net> wrote:

So, this seems like a case where a built-in role would be
well-justified. I don't really believe in built-in roles as a way of
bundling related permissions; I know you do, but I don't. I'd rather
see the individual function permissions granted individually. But
here you are talking about a variable level of access to the same
function, depending on role. And for that it seems to me that a
built-in role has a lot more to recommend it in that case than it does
in the other. If you have been granted pg_whack, you can signal any
process on the system; otherwise just your own. Those checks are
internal to pg_terminate_backend/pg_cancel_backend so GRANT is not a
substitute.

If we extend this into the future then it seems to potentially fall
afoul of Noah's concern that we're going to end up with two different
ways of saying GRANT EXECUTE X ON Y. Consider the more complicated case
of pg_stat_activity, where values are shown or hidden based on who the
current role is. The policy system only supports whole-row, today, but
the question has already come up, both on the lists and off, of support
for hiding individual column values for certain rows, exactly as we do
today for pg_stat_activity. Once we reach that point, we'll have a way
to GRANT out these rights and a default role which just has them.

Well, I'm not saying that predefined rolls are the *only* way to solve
a problem like this, but I think they're one way and I don't clearly
see that something else is better. However, my precise point is that
we should *not* have predefined rolls that precisely duplicate the
result of GRANT EXECUTE X ON Y. Having predefined rolls that change
the behavior of the system in other ways is a different thing. So I
don't see how this falls afoul of Noah's concern. (If it does,
perhaps he can clarify.)

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

What got me thinking along these lines was a question posed by Bruce
(Bruce, feel free to chime in if I've misunderstood) to me at SCALE
where we were chatting a bit about this, which was- how could we extend
GRANT to support the permissions that we actually wish
pg_terminate_backend() and pg_cancel_backend() to have, instead of using
a default role? I didn't think too much on it at the time as it strikes
me as a pretty narrow use-case and requiring quite a bit of complexity
to get right, but there again, I'd certainly view a system where the
user is allowed to have pg_start_backup() rights but not
pg_stop_backup() as being quite a small use-case also, yet that's the
direction we're largely going in with this discussion.

Well, sure, in that largely artificial example it's not hard to say
ha, ha, silly, but the actual examples we looked at upthread were much
less obviously silly. There was plenty of room for argument about the
precise contours of each predefined role.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#178Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#177)
Re: Additional role attributes && superuser review

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net> wrote:

So, this seems like a case where a built-in role would be
well-justified. I don't really believe in built-in roles as a way of
bundling related permissions; I know you do, but I don't. I'd rather
see the individual function permissions granted individually. But
here you are talking about a variable level of access to the same
function, depending on role. And for that it seems to me that a
built-in role has a lot more to recommend it in that case than it does
in the other. If you have been granted pg_whack, you can signal any
process on the system; otherwise just your own. Those checks are
internal to pg_terminate_backend/pg_cancel_backend so GRANT is not a
substitute.

If we extend this into the future then it seems to potentially fall
afoul of Noah's concern that we're going to end up with two different
ways of saying GRANT EXECUTE X ON Y. Consider the more complicated case
of pg_stat_activity, where values are shown or hidden based on who the
current role is. The policy system only supports whole-row, today, but
the question has already come up, both on the lists and off, of support
for hiding individual column values for certain rows, exactly as we do
today for pg_stat_activity. Once we reach that point, we'll have a way
to GRANT out these rights and a default role which just has them.

Well, I'm not saying that predefined rolls are the *only* way to solve
a problem like this, but I think they're one way and I don't clearly
see that something else is better. However, my precise point is that
we should *not* have predefined rolls that precisely duplicate the
result of GRANT EXECUTE X ON Y. Having predefined rolls that change
the behavior of the system in other ways is a different thing. So I
don't see how this falls afoul of Noah's concern. (If it does,
perhaps he can clarify.)

Apologies if it seems like what I'm getting at is obtuse but I'm trying
to generalize this, to provide guidance on how to handle the larger set
of privileges.

If I'm following correctly, having default roles for cases where the
role is specifically for something more than 'GRANT EXECUTE X ON Y' (or
a set of such commands..?) makes sense. Going back to the list of
roles, that would mean that default roles:

pg_monitor

Allows roles granted more information from pg_stat_activity. Can't be
just a regular non-default-role right as we don't, currently, have a
way to say "filter out the values of certain columns on certain rows,
but not on others."

(There's a question here though- for the privileges which will be
directly GRANT'able, should we GRANT those to pg_monitor, or have
pg_monitor only provide unfiltered access to pg_stat_activity, or..?
Further, if it's only for pg_stat_activity, should we name it
something else?)

pg_signal_backend

Allows roles to signal other backend processes beyond those backends
which are owned by a user they are a role member of. Can't be a
regular non-default-role right as we don't, currently, have any way to
GRANT rights to send signals only to backends you own or are a member
of.

pg_replication

Allows roles to use the various replication functions. Can't be a
regular non-default-role right as the REPLICATION role attribute
allows access to those functions and the GRANT system has no way of
saying "allow access to these functions if they have role attribute
X."

Make sense, as these are cases where we can't simply write GRANT
statements and get the same result, but we don't need (or at least,
shouldn't have without supporting GRANT on catalog objects and agreement
on what they're intended for):

pg_backup

pg_start_backup(), pg_stop_backup(), pg_switch_xlog(), and
pg_create_restore_point() will all be managed by the normal GRANT
system and therefore we don't need a default role for those use-cases.

pg_file_settings

pg_file_settings() function and pg_file_settings view will be managed
by the normal GRANT system and therefore we don't need a default role
for those use-cases.

pg_replay

pg_xlog_replay_pause(), and pg_xlog_replay_resume() will be managed
by the normal GRANT system and therefore we don't need a default role
for those use-cases.

pg_rotate_logfile

pg_rotate_logfile() will be managed by the normal GRANT system and
therefore we don't need a default role for that use-cases.

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already written
a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable. When I was talking about moving goalposts, I
was saying that if we don't want default roles until the only thing
they're doing is being GRANT'd rights which administrators could GRANT
or create policies for themselves (which may one day be the case) then
that would imply a much larger amount of effort (support for hiding
columns based on policies, and perhaps even some way to GRANT access to
functions to operate only against other backends which are controlled by
a role of which you are a member).

What got me thinking along these lines was a question posed by Bruce
(Bruce, feel free to chime in if I've misunderstood) to me at SCALE
where we were chatting a bit about this, which was- how could we extend
GRANT to support the permissions that we actually wish
pg_terminate_backend() and pg_cancel_backend() to have, instead of using
a default role? I didn't think too much on it at the time as it strikes
me as a pretty narrow use-case and requiring quite a bit of complexity
to get right, but there again, I'd certainly view a system where the
user is allowed to have pg_start_backup() rights but not
pg_stop_backup() as being quite a small use-case also, yet that's the
direction we're largely going in with this discussion.

Well, sure, in that largely artificial example it's not hard to say
ha, ha, silly, but the actual examples we looked at upthread were much
less obviously silly. There was plenty of room for argument about the
precise contours of each predefined role.

I'm sure I'm looking at this through rosy colored goggles, but going
back over this thread, which started in October of 2014, and the other
one which I started, the questions which have come up around the rights
granted the default roles have been:

People would really like a default role that allowed pg_dump to always
work and maybe we should name 'pg_backup' something else, since it's
different from that.

Shouldn't we restrict access to the various pg_*_xlog_location()
functions? (I continue to agree that we should, but that's really an
independent discussion..)

The only specific discussions or questions about the permissions
included in the default roles, based on my ~1h review of the threads,
were from Michael regarding pg_switch_xlog(), who ultimately agreed that
it was fine as part of pg_backup, and from Fujii who suggested that
pg_monitor also have access to pgstattuple(), which is a contrib module
(and hence, I hadn't really thought about it, to be frank, tho it could
certainly be modified to work with a pg_monitor role). The other
concerns raised have not included specific issues or questions about the
privileges included but rather contended that there may be issues.

That's not to say that there couldn't be issues raised, but I guess I
don't see that many possible combinations for the sets of rights
included, which are the only ones sensible to consider based on my
review of the "if (!superuser()) ereport()" calls in the backend (from:
/messages/by-id/20141015052259.GG28859@tamriel.snowman.net).

Thanks!

Stephen

#179Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#178)
Re: Additional role attributes && superuser review

On Fri, Jan 29, 2016 at 6:37 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net>

wrote:

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already written
a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable.

Is it already posted somewhere? I don't recall seeing it. Robert and Noah
have a point that this would be useful for users who would like to dump
GRANT/REVOKE rights on system functions & all, using a new option in
pg_dumpall, say --with-system-acl or --with-system-privileges. If at least
the three of you are agreeing here I think that we should try to move at
least toward this goal first. That seems a largely doable goal for 9.6. For
the set of default roles, there is clearly no clear consensus regarding
what each role should do or not, and under which limitation it should
operate.
--
Michael

#180Stephen Frost
sfrost@snowman.net
In reply to: Michael Paquier (#179)
Re: Additional role attributes && superuser review

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Fri, Jan 29, 2016 at 6:37 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net>

wrote:

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already written
a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable.

Is it already posted somewhere? I don't recall seeing it. Robert and Noah
have a point that this would be useful for users who would like to dump
GRANT/REVOKE rights on system functions & all, using a new option in
pg_dumpall, say --with-system-acl or --with-system-privileges.

Multiple versions were posted on this thread. I don't fault you for not
finding it, this thread is a bit long in the tooth. The one I'm
currently working from is:

/messages/by-id/attachment/38049/catalog_function_acls_v4.patch

I'm going to split up the pg_dump changes and the backend changes, as
they can logically go in independently (though without both, we're not
moving the needle very far with regards to what administrators can do).

If at least
the three of you are agreeing here I think that we should try to move at
least toward this goal first. That seems a largely doable goal for 9.6. For
the set of default roles, there is clearly no clear consensus regarding
what each role should do or not, and under which limitation it should
operate.

I'm trying to work towards a consensus on the default roles, hence the
questions and discussion posed in the email you replied to.

Thanks!

Stephen

#181Michael Paquier
michael.paquier@gmail.com
In reply to: Stephen Frost (#180)
Re: Additional role attributes && superuser review

On Fri, Jan 29, 2016 at 11:41 PM, Stephen Frost <sfrost@snowman.net> wrote:

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Fri, Jan 29, 2016 at 6:37 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net>

wrote:

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already written
a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable.

Is it already posted somewhere? I don't recall seeing it. Robert and Noah
have a point that this would be useful for users who would like to dump
GRANT/REVOKE rights on system functions & all, using a new option in
pg_dumpall, say --with-system-acl or --with-system-privileges.

Multiple versions were posted on this thread. I don't fault you for not
finding it, this thread is a bit long in the tooth. The one I'm
currently working from is:

/messages/by-id/attachment/38049/catalog_function_acls_v4.patch

I'm going to split up the pg_dump changes and the backend changes, as
they can logically go in independently (though without both, we're not
moving the needle very far with regards to what administrators can do).

OK. Looks like a good way to move forward to me.

If at least
the three of you are agreeing here I think that we should try to move at
least toward this goal first. That seems a largely doable goal for 9.6. For
the set of default roles, there is clearly no clear consensus regarding
what each role should do or not, and under which limitation it should
operate.

I'm trying to work towards a consensus on the default roles, hence the
questions and discussion posed in the email you replied to.

So the current CF entry should be marked as returned with feedback
perhaps? What do you think? Once patches are ready for the default
roles in backend and for pg_dump, we could then work on reviewing them
separately.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#182Craig Ringer
craig@2ndquadrant.com
In reply to: Stephen Frost (#180)
Re: Additional role attributes && superuser review

On 29 January 2016 at 22:41, Stephen Frost <sfrost@snowman.net> wrote:

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Fri, Jan 29, 2016 at 6:37 AM, Stephen Frost <sfrost@snowman.net>

wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net>

wrote:

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far

though, if

we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already

written

a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable.

Is it already posted somewhere? I don't recall seeing it. Robert and Noah
have a point that this would be useful for users who would like to dump
GRANT/REVOKE rights on system functions & all, using a new option in
pg_dumpall, say --with-system-acl or --with-system-privileges.

Multiple versions were posted on this thread. I don't fault you for not
finding it, this thread is a bit long in the tooth. The one I'm
currently working from is

It strikes me that this thread would possibly benefit from a wiki page
outlining the permissions, overall concepts, etc, as it's getting awfully
hard to follow.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

#183Michael Paquier
michael.paquier@gmail.com
In reply to: Craig Ringer (#182)
Re: Additional role attributes && superuser review

On Sun, Jan 31, 2016 at 5:32 AM, Craig Ringer <craig@2ndquadrant.com> wrote:

On 29 January 2016 at 22:41, Stephen Frost <sfrost@snowman.net> wrote:

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Fri, Jan 29, 2016 at 6:37 AM, Stephen Frost <sfrost@snowman.net>
wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net>

wrote:

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far
though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already
written
a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable.

Is it already posted somewhere? I don't recall seeing it. Robert and
Noah
have a point that this would be useful for users who would like to dump
GRANT/REVOKE rights on system functions & all, using a new option in
pg_dumpall, say --with-system-acl or --with-system-privileges.

Multiple versions were posted on this thread. I don't fault you for not
finding it, this thread is a bit long in the tooth. The one I'm
currently working from is

It strikes me that this thread would possibly benefit from a wiki page
outlining the permissions, overall concepts, etc, as it's getting awfully
hard to follow.

+1. This has proved to be very beneficial for UPSERT.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#184Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#183)
Re: Additional role attributes && superuser review

On Sun, Jan 31, 2016 at 7:55 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Sun, Jan 31, 2016 at 5:32 AM, Craig Ringer <craig@2ndquadrant.com> wrote:

On 29 January 2016 at 22:41, Stephen Frost <sfrost@snowman.net> wrote:

Michael,

* Michael Paquier (michael.paquier@gmail.com) wrote:

On Fri, Jan 29, 2016 at 6:37 AM, Stephen Frost <sfrost@snowman.net>
wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Thu, Jan 28, 2016 at 11:04 AM, Stephen Frost <sfrost@snowman.net>

wrote:

Personally, I don't have any particular issue having both, but the
desire was stated that it would be better to have the regular
GRANT EXECUTE ON catalog_func() working before we consider having
default roles for same. That moves the goal posts awful far
though, if
we're to stick with that and consider how we might extend the GRANT
system in the future.

I don't think it moves the goal posts all that far. Convincing
pg_dump to dump grants on system functions shouldn't be a crazy large
patch.

I wasn't clear as to what I was referring to here. I've already
written
a patch to pg_dump to support grants on system objects and agree that
it's at least reasonable.

Is it already posted somewhere? I don't recall seeing it. Robert and
Noah
have a point that this would be useful for users who would like to dump
GRANT/REVOKE rights on system functions & all, using a new option in
pg_dumpall, say --with-system-acl or --with-system-privileges.

Multiple versions were posted on this thread. I don't fault you for not
finding it, this thread is a bit long in the tooth. The one I'm
currently working from is

It strikes me that this thread would possibly benefit from a wiki page
outlining the permissions, overall concepts, etc, as it's getting awfully
hard to follow.

+1. This has proved to be very beneficial for UPSERT.

I am marking this patch as returned with feedback per the current
status of this thread.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#185Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#178)
Re: Additional role attributes && superuser review

On Thu, Jan 28, 2016 at 4:37 PM, Stephen Frost <sfrost@snowman.net> wrote:

pg_monitor

Allows roles granted more information from pg_stat_activity. Can't be
just a regular non-default-role right as we don't, currently, have a
way to say "filter out the values of certain columns on certain rows,
but not on others."

(There's a question here though- for the privileges which will be
directly GRANT'able, should we GRANT those to pg_monitor, or have
pg_monitor only provide unfiltered access to pg_stat_activity, or..?
Further, if it's only for pg_stat_activity, should we name it
something else?)

I endorse this proposed role. I'd have it just grant access to
pg_stat_activity but keep the name pg_monitor so that it could apply
to other similar things in the future, but there might be other good
alternatives too.

pg_signal_backend

Allows roles to signal other backend processes beyond those backends
which are owned by a user they are a role member of. Can't be a
regular non-default-role right as we don't, currently, have any way to
GRANT rights to send signals only to backends you own or are a member
of.

I also endorse this.

pg_replication

Allows roles to use the various replication functions. Can't be a
regular non-default-role right as the REPLICATION role attribute
allows access to those functions and the GRANT system has no way of
saying "allow access to these functions if they have role attribute
X."

Make sense, as these are cases where we can't simply write GRANT
statements and get the same result, but we don't need (or at least,
shouldn't have without supporting GRANT on catalog objects and agreement
on what they're intended for):

This seems like it could be reshuffled so that it can be done with
GRANT. Therefore, I don't endorse this.

pg_backup

pg_start_backup(), pg_stop_backup(), pg_switch_xlog(), and
pg_create_restore_point() will all be managed by the normal GRANT
system and therefore we don't need a default role for those use-cases.

Agreed.

pg_file_settings

pg_file_settings() function and pg_file_settings view will be managed
by the normal GRANT system and therefore we don't need a default role
for those use-cases.

Agreed.

pg_replay

pg_xlog_replay_pause(), and pg_xlog_replay_resume() will be managed
by the normal GRANT system and therefore we don't need a default role
for those use-cases.

Agreed.

pg_rotate_logfile

pg_rotate_logfile() will be managed by the normal GRANT system and
therefore we don't need a default role for that use-cases.

Agreed.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#186Noah Misch
noah@leadboat.com
In reply to: Robert Haas (#185)
Re: Additional role attributes && superuser review

On Wed, Feb 03, 2016 at 01:44:28PM -0500, Robert Haas wrote:

On Thu, Jan 28, 2016 at 4:37 PM, Stephen Frost <sfrost@snowman.net> wrote:

pg_monitor

Allows roles granted more information from pg_stat_activity. Can't be
just a regular non-default-role right as we don't, currently, have a
way to say "filter out the values of certain columns on certain rows,
but not on others."

(There's a question here though- for the privileges which will be
directly GRANT'able, should we GRANT those to pg_monitor, or have
pg_monitor only provide unfiltered access to pg_stat_activity, or..?
Further, if it's only for pg_stat_activity, should we name it
something else?)

I endorse this proposed role. I'd have it just grant access to
pg_stat_activity but keep the name pg_monitor so that it could apply
to other similar things in the future, but there might be other good
alternatives too.

-1 for adding any pg_monitor role. That name belongs to a role allowing, from
inception, more than just full pg_stat_activity access. The last, vigorous
effort to define its scope failed; there may not exist one notion of
"monitoring" operations qualified to claim this name. At least, the community
lacks the ability to design it in the foreseeable future.

pg_signal_backend

Allows roles to signal other backend processes beyond those backends
which are owned by a user they are a role member of. Can't be a
regular non-default-role right as we don't, currently, have any way to
GRANT rights to send signals only to backends you own or are a member
of.

I also endorse this.

+1. This role has a clear scope and a name denoting that scope, so it's a
sound way to introduce built-in role infrastructure. I like the concept of
built-in roles. One can inherit them via user-defined roles and use NOINHERIT
to regulate that. One can grant WITH ADMIN OPTION, something role options
like REPLICATION don't offer. It feels more scalable than defining role
options, and it's a technique non-core code can imitate.

I haven't decided whether it's a problem that members of built-in roles can
create objects owned by those roles.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers