* 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,
signature.asc
Description: Digital signature