* Tom Lane (t...@sss.pgh.pa.us) wrote:
> Stephen Frost <sfr...@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
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,

Attachment: signature.asc
Description: Digital signature

Reply via email to