]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/pgstatfuncs.c
Add support for EUI-64 MAC addresses as macaddr8
[postgresql] / src / backend / utils / adt / pgstatfuncs.c
index 2b8f5ee1e60b6fbb117a353c60620c7cdf5348dd..a987d0d62143a629491b3878d6389fdc220f5e6b 100644 (file)
@@ -3,7 +3,7 @@
  * pgstatfuncs.c
  *       Functions for accessing the statistics collector data
  *
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 
 #include "access/htup_details.h"
 #include "catalog/pg_type.h"
+#include "common/ip.h"
 #include "funcapi.h"
-#include "libpq/ip.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/inet.h"
 #include "utils/timestamp.h"
 
-/* bogus ... these externs should be in a header file */
-extern Datum pg_stat_get_numscans(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_tuples_returned(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_tuples_inserted(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_tuples_updated(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_tuples_deleted(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_tuples_hot_updated(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_live_tuples(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_dead_tuples(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_mod_since_analyze(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_blocks_hit(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_last_vacuum_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_last_autovacuum_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_last_analyze_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_last_autoanalyze_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_vacuum_count(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_autovacuum_count(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_analyze_count(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_autoanalyze_count(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_get_function_calls(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_function_total_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
-extern Datum pg_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_get_backend_userid(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_activity(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_waiting(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_start(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_backend_client_port(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_get_db_numbackends(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_xact_commit(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_xact_rollback(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_tuples_returned(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_tuples_fetched(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_tuples_inserted(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_tuples_updated(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_tuples_deleted(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_conflict_tablespace(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_conflict_lock(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_conflict_snapshot(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_conflict_bufferpin(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_conflict_startup_deadlock(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_conflict_all(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_deadlocks(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_stat_reset_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_temp_files(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_temp_bytes(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_blk_read_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_db_blk_write_time(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_get_bgwriter_timed_checkpoints(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_bgwriter_requested_checkpoints(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_checkpoint_write_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_checkpoint_sync_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_bgwriter_buf_written_checkpoints(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_bgwriter_buf_written_clean(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_bgwriter_maxwritten_clean(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_bgwriter_stat_reset_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_buf_written_backend(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_buf_fsync_backend(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_buf_alloc(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_get_xact_numscans(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_tuples_returned(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_tuples_fetched(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_tuples_inserted(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_tuples_updated(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_tuples_deleted(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_tuples_hot_updated(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_blocks_hit(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_get_xact_function_calls(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_function_total_time(PG_FUNCTION_ARGS);
-extern Datum pg_stat_get_xact_function_self_time(PG_FUNCTION_ARGS);
-
-extern Datum pg_stat_clear_snapshot(PG_FUNCTION_ARGS);
-extern Datum pg_stat_reset(PG_FUNCTION_ARGS);
-extern Datum pg_stat_reset_shared(PG_FUNCTION_ARGS);
-extern Datum pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS);
-extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS);
+#define UINT32_ACCESS_ONCE(var)                 ((uint32)(*((volatile uint32 *)&(var))))
 
 /* Global bgwriter statistics, from bgwriter.c */
 extern PgStat_MsgBgWriter bgwriterStats;
@@ -520,132 +430,190 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
        }
 }
 
+/*
+ * Returns command progress information for the named command.
+ */
 Datum
-pg_stat_get_activity(PG_FUNCTION_ARGS)
-{
-       FuncCallContext *funcctx;
+pg_stat_get_progress_info(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_PROGRESS_COLS      PGSTAT_NUM_PROGRESS_PARAM + 3
+       int                     num_backends = pgstat_fetch_stat_numbackends();
+       int                     curr_backend;
+       char       *cmd = text_to_cstring(PG_GETARG_TEXT_PP(0));
+       ProgressCommandType cmdtype;
+       TupleDesc       tupdesc;
+       Tuplestorestate *tupstore;
+       ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+       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");
+
+       /* Translate command name into command type code. */
+       if (pg_strcasecmp(cmd, "VACUUM") == 0)
+               cmdtype = PROGRESS_COMMAND_VACUUM;
+       else
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("invalid command name: \"%s\"", cmd)));
 
-       if (SRF_IS_FIRSTCALL())
+       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);
+
+       /* 1-based index */
+       for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
        {
-               MemoryContext oldcontext;
-               TupleDesc       tupdesc;
+               LocalPgBackendStatus *local_beentry;
+               PgBackendStatus *beentry;
+               Datum           values[PG_STAT_GET_PROGRESS_COLS];
+               bool            nulls[PG_STAT_GET_PROGRESS_COLS];
+               int                     i;
 
-               funcctx = SRF_FIRSTCALL_INIT();
+               MemSet(values, 0, sizeof(values));
+               MemSet(nulls, 0, sizeof(nulls));
+
+               local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+
+               if (!local_beentry)
+                       continue;
+
+               beentry = &local_beentry->backendStatus;
 
-               oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-               tupdesc = CreateTemplateTupleDesc(14, 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);
-
-               funcctx->tuple_desc = BlessTupleDesc(tupdesc);
-
-               funcctx->user_fctx = palloc0(sizeof(int));
-               if (PG_ARGISNULL(0))
+               /*
+                * Report values for only those backends which are running the given
+                * command.
+                */
+               if (!beentry || beentry->st_progress_command != cmdtype)
+                       continue;
+
+               /* Value available to all callers */
+               values[0] = Int32GetDatum(beentry->st_procpid);
+               values[1] = ObjectIdGetDatum(beentry->st_databaseid);
+
+               /* show rest of the values including relid only to role members */
+               if (has_privs_of_role(GetUserId(), beentry->st_userid))
                {
-                       /* Get all backends */
-                       funcctx->max_calls = pgstat_fetch_stat_numbackends();
+                       values[2] = ObjectIdGetDatum(beentry->st_progress_command_target);
+                       for (i = 0; i < PGSTAT_NUM_PROGRESS_PARAM; i++)
+                               values[i + 3] = Int64GetDatum(beentry->st_progress_param[i]);
                }
                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;
+                       nulls[2] = true;
+                       for (i = 0; i < PGSTAT_NUM_PROGRESS_PARAM; i++)
+                               nulls[i + 3] = true;
                }
 
-               MemoryContextSwitchTo(oldcontext);
+               tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
 
-       /* stuff done on every call of the function */
-       funcctx = SRF_PERCALL_SETUP();
+       /* clean up and return the tuplestore */
+       tuplestore_donestoring(tupstore);
+
+       return (Datum) 0;
+}
 
-       if (funcctx->call_cntr < funcctx->max_calls)
+/*
+ * Returns activity of PG backends.
+ */
+Datum
+pg_stat_get_activity(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_ACTIVITY_COLS      23
+       int                     num_backends = pgstat_fetch_stat_numbackends();
+       int                     curr_backend;
+       int                     pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+       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);
+
+       /* 1-based index */
+       for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
        {
                /* for each row */
-               Datum           values[14];
-               bool            nulls[14];
-               HeapTuple       tuple;
+               Datum           values[PG_STAT_GET_ACTIVITY_COLS];
+               bool            nulls[PG_STAT_GET_ACTIVITY_COLS];
+               LocalPgBackendStatus *local_beentry;
                PgBackendStatus *beentry;
+               PGPROC     *proc;
+               const char *wait_event_type;
+               const char *wait_event;
 
                MemSet(values, 0, sizeof(values));
                MemSet(nulls, 0, sizeof(nulls));
 
-               if (*(int *) (funcctx->user_fctx) > 0)
-               {
-                       /* Get specific pid slot */
-                       beentry = pgstat_fetch_stat_beentry(*(int *) (funcctx->user_fctx));
-               }
-               else
-               {
-                       /* Get the next one in the list */
-                       beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1);            /* 1-based index */
-               }
-               if (!beentry)
+               /* Get the next one in the list */
+               local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+               if (!local_beentry)
                {
                        int                     i;
 
-                       for (i = 0; i < sizeof(nulls) / sizeof(nulls[0]); i++)
+                       /* Ignore missing entries if looking for specific PID */
+                       if (pid != -1)
+                               continue;
+
+                       for (i = 0; i < lengthof(nulls); i++)
                                nulls[i] = true;
 
                        nulls[5] = false;
                        values[5] = CStringGetTextDatum("<backend information not available>");
 
-                       tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-                       SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+                       tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+                       continue;
                }
 
+               beentry = &local_beentry->backendStatus;
+
+               /* If looking for specific PID, ignore all the others */
+               if (pid != -1 && beentry->st_procpid != pid)
+                       continue;
+
                /* Values available to all callers */
                values[0] = ObjectIdGetDatum(beentry->st_databaseid);
                values[1] = Int32GetDatum(beentry->st_procpid);
@@ -655,8 +623,33 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                else
                        nulls[3] = true;
 
-               /* Values only available to same user or superuser */
-               if (superuser() || beentry->st_userid == GetUserId())
+               if (TransactionIdIsValid(local_beentry->backend_xid))
+                       values[15] = TransactionIdGetDatum(local_beentry->backend_xid);
+               else
+                       nulls[15] = true;
+
+               if (TransactionIdIsValid(local_beentry->backend_xmin))
+                       values[16] = TransactionIdGetDatum(local_beentry->backend_xmin);
+               else
+                       nulls[16] = true;
+
+               if (beentry->st_ssl)
+               {
+                       values[17] = BoolGetDatum(true);        /* ssl */
+                       values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
+                       values[19] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
+                       values[20] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
+                       values[21] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
+                       values[22] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn);
+               }
+               else
+               {
+                       values[17] = BoolGetDatum(false);       /* ssl */
+                       nulls[18] = nulls[19] = nulls[20] = nulls[21] = nulls[22] = true;
+               }
+
+               /* Values only available to role member */
+               if (has_privs_of_role(GetUserId(), beentry->st_userid))
                {
                        SockAddr        zero_clientaddr;
 
@@ -686,36 +679,61 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                        }
 
                        values[5] = CStringGetTextDatum(beentry->st_activity);
-                       values[6] = BoolGetDatum(beentry->st_waiting);
 
-                       if (beentry->st_xact_start_timestamp != 0)
-                               values[7] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
+                       proc = BackendPidGetProc(beentry->st_procpid);
+                       if (proc != NULL)
+                       {
+                               uint32          raw_wait_event;
+
+                               raw_wait_event = UINT32_ACCESS_ONCE(proc->wait_event_info);
+                               wait_event_type = pgstat_get_wait_event_type(raw_wait_event);
+                               wait_event = pgstat_get_wait_event(raw_wait_event);
+
+                       }
+                       else
+                       {
+                               wait_event_type = NULL;
+                               wait_event = NULL;
+                       }
+
+                       if (wait_event_type)
+                               values[6] = CStringGetTextDatum(wait_event_type);
+                       else
+                               nulls[6] = true;
+
+                       if (wait_event)
+                               values[7] = CStringGetTextDatum(wait_event);
                        else
                                nulls[7] = true;
 
-                       if (beentry->st_activity_start_timestamp != 0)
-                               values[8] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
+                       if (beentry->st_xact_start_timestamp != 0)
+                               values[8] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
                        else
                                nulls[8] = true;
 
-                       if (beentry->st_proc_start_timestamp != 0)
-                               values[9] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
+                       if (beentry->st_activity_start_timestamp != 0)
+                               values[9] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
                        else
                                nulls[9] = true;
 
-                       if (beentry->st_state_start_timestamp != 0)
-                               values[10] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
+                       if (beentry->st_proc_start_timestamp != 0)
+                               values[10] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
                        else
                                nulls[10] = true;
 
+                       if (beentry->st_state_start_timestamp != 0)
+                               values[11] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
+                       else
+                               nulls[11] = true;
+
                        /* A zeroed client addr means we don't know */
                        memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
                        if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
                                           sizeof(zero_clientaddr)) == 0)
                        {
-                               nulls[11] = true;
                                nulls[12] = true;
                                nulls[13] = true;
+                               nulls[14] = true;
                        }
                        else
                        {
@@ -739,19 +757,20 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                                        if (ret == 0)
                                        {
                                                clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
-                                               values[11] = DirectFunctionCall1(inet_in,
+                                               values[12] = DirectFunctionCall1(inet_in,
                                                                                           CStringGetDatum(remote_host));
-                                               if (beentry->st_clienthostname)
-                                                       values[12] = CStringGetTextDatum(beentry->st_clienthostname);
+                                               if (beentry->st_clienthostname &&
+                                                       beentry->st_clienthostname[0])
+                                                       values[13] = CStringGetTextDatum(beentry->st_clienthostname);
                                                else
-                                                       nulls[12] = true;
-                                               values[13] = Int32GetDatum(atoi(remote_port));
+                                                       nulls[13] = true;
+                                               values[14] = Int32GetDatum(atoi(remote_port));
                                        }
                                        else
                                        {
-                                               nulls[11] = true;
                                                nulls[12] = true;
                                                nulls[13] = true;
+                                               nulls[14] = true;
                                        }
                                }
                                else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX)
@@ -762,16 +781,16 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                                         * connections we have no permissions to view, or with
                                         * errors.
                                         */
-                                       nulls[11] = true;
                                        nulls[12] = true;
-                                       values[13] = DatumGetInt32(-1);
+                                       nulls[13] = true;
+                                       values[14] = DatumGetInt32(-1);
                                }
                                else
                                {
                                        /* Unknown address type, should never happen */
-                                       nulls[11] = true;
                                        nulls[12] = true;
                                        nulls[13] = true;
+                                       nulls[14] = true;
                                }
                        }
                }
@@ -788,17 +807,20 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                        nulls[11] = true;
                        nulls[12] = true;
                        nulls[13] = true;
+                       nulls[14] = true;
                }
 
-               tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+               tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 
-               SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
-       }
-       else
-       {
-               /* nothing left */
-               SRF_RETURN_DONE(funcctx);
+               /* If only a single backend was requested, and we found it, break. */
+               if (pid != -1)
+                       break;
        }
+
+       /* clean up and return the tuplestore */
+       tuplestore_donestoring(tupstore);
+
+       return (Datum) 0;
 }
 
 
@@ -857,7 +879,7 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
 
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
                activity = "<backend information not available>";
-       else if (!superuser() && beentry->st_userid != GetUserId())
+       else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
                activity = "<insufficient privilege>";
        else if (*(beentry->st_activity) == '\0')
                activity = "<command string not enabled>";
@@ -867,23 +889,46 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS)
        PG_RETURN_TEXT_P(cstring_to_text(activity));
 }
 
-
 Datum
-pg_stat_get_backend_waiting(PG_FUNCTION_ARGS)
+pg_stat_get_backend_wait_event_type(PG_FUNCTION_ARGS)
 {
        int32           beid = PG_GETARG_INT32(0);
-       bool            result;
        PgBackendStatus *beentry;
+       PGPROC     *proc;
+       const char *wait_event_type = NULL;
 
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
-               PG_RETURN_NULL();
+               wait_event_type = "<backend information not available>";
+       else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+               wait_event_type = "<insufficient privilege>";
+       else if ((proc = BackendPidGetProc(beentry->st_procpid)) != NULL)
+               wait_event_type = pgstat_get_wait_event_type(proc->wait_event_info);
 
-       if (!superuser() && beentry->st_userid != GetUserId())
+       if (!wait_event_type)
                PG_RETURN_NULL();
 
-       result = beentry->st_waiting;
+       PG_RETURN_TEXT_P(cstring_to_text(wait_event_type));
+}
+
+Datum
+pg_stat_get_backend_wait_event(PG_FUNCTION_ARGS)
+{
+       int32           beid = PG_GETARG_INT32(0);
+       PgBackendStatus *beentry;
+       PGPROC     *proc;
+       const char *wait_event = NULL;
+
+       if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
+               wait_event = "<backend information not available>";
+       else if (!has_privs_of_role(GetUserId(), beentry->st_userid))
+               wait_event = "<insufficient privilege>";
+       else if ((proc = BackendPidGetProc(beentry->st_procpid)) != NULL)
+               wait_event = pgstat_get_wait_event(proc->wait_event_info);
+
+       if (!wait_event)
+               PG_RETURN_NULL();
 
-       PG_RETURN_BOOL(result);
+       PG_RETURN_TEXT_P(cstring_to_text(wait_event));
 }
 
 
@@ -897,7 +942,7 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS)
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
                PG_RETURN_NULL();
 
-       if (!superuser() && beentry->st_userid != GetUserId())
+       if (!has_privs_of_role(GetUserId(), beentry->st_userid))
                PG_RETURN_NULL();
 
        result = beentry->st_activity_start_timestamp;
@@ -923,7 +968,7 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS)
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
                PG_RETURN_NULL();
 
-       if (!superuser() && beentry->st_userid != GetUserId())
+       if (!has_privs_of_role(GetUserId(), beentry->st_userid))
                PG_RETURN_NULL();
 
        result = beentry->st_xact_start_timestamp;
@@ -945,7 +990,7 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS)
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
                PG_RETURN_NULL();
 
-       if (!superuser() && beentry->st_userid != GetUserId())
+       if (!has_privs_of_role(GetUserId(), beentry->st_userid))
                PG_RETURN_NULL();
 
        result = beentry->st_proc_start_timestamp;
@@ -969,7 +1014,7 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
                PG_RETURN_NULL();
 
-       if (!superuser() && beentry->st_userid != GetUserId())
+       if (!has_privs_of_role(GetUserId(), beentry->st_userid))
                PG_RETURN_NULL();
 
        /* A zeroed client addr means we don't know */
@@ -1016,7 +1061,7 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS)
        if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL)
                PG_RETURN_NULL();
 
-       if (!superuser() && beentry->st_userid != GetUserId())
+       if (!has_privs_of_role(GetUserId(), beentry->st_userid))
                PG_RETURN_NULL();
 
        /* A zeroed client addr means we don't know */
@@ -1662,6 +1707,13 @@ pg_stat_get_xact_function_self_time(PG_FUNCTION_ARGS)
 }
 
 
+/* Get the timestamp of the current statistics snapshot */
+Datum
+pg_stat_get_snapshot_timestamp(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->stats_timestamp);
+}
+
 /* Discard the active statistics snapshot */
 Datum
 pg_stat_clear_snapshot(PG_FUNCTION_ARGS)
@@ -1692,7 +1744,7 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS)
        PG_RETURN_VOID();
 }
 
-/* Reset a single counter in the current database */
+/* Reset a single counter in the current database */
 Datum
 pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS)
 {
@@ -1712,3 +1764,70 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS)
 
        PG_RETURN_VOID();
 }
+
+Datum
+pg_stat_get_archiver(PG_FUNCTION_ARGS)
+{
+       TupleDesc       tupdesc;
+       Datum           values[7];
+       bool            nulls[7];
+       PgStat_ArchiverStats *archiver_stats;
+
+       /* Initialise values and NULL flags arrays */
+       MemSet(values, 0, sizeof(values));
+       MemSet(nulls, 0, sizeof(nulls));
+
+       /* Initialise attributes information in the tuple descriptor */
+       tupdesc = CreateTemplateTupleDesc(7, false);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 1, "archived_count",
+                                          INT8OID, -1, 0);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 2, "last_archived_wal",
+                                          TEXTOID, -1, 0);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 3, "last_archived_time",
+                                          TIMESTAMPTZOID, -1, 0);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 4, "failed_count",
+                                          INT8OID, -1, 0);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 5, "last_failed_wal",
+                                          TEXTOID, -1, 0);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 6, "last_failed_time",
+                                          TIMESTAMPTZOID, -1, 0);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 7, "stats_reset",
+                                          TIMESTAMPTZOID, -1, 0);
+
+       BlessTupleDesc(tupdesc);
+
+       /* Get statistics about the archiver process */
+       archiver_stats = pgstat_fetch_stat_archiver();
+
+       /* Fill values and NULLs */
+       values[0] = Int64GetDatum(archiver_stats->archived_count);
+       if (*(archiver_stats->last_archived_wal) == '\0')
+               nulls[1] = true;
+       else
+               values[1] = CStringGetTextDatum(archiver_stats->last_archived_wal);
+
+       if (archiver_stats->last_archived_timestamp == 0)
+               nulls[2] = true;
+       else
+               values[2] = TimestampTzGetDatum(archiver_stats->last_archived_timestamp);
+
+       values[3] = Int64GetDatum(archiver_stats->failed_count);
+       if (*(archiver_stats->last_failed_wal) == '\0')
+               nulls[4] = true;
+       else
+               values[4] = CStringGetTextDatum(archiver_stats->last_failed_wal);
+
+       if (archiver_stats->last_failed_timestamp == 0)
+               nulls[5] = true;
+       else
+               values[5] = TimestampTzGetDatum(archiver_stats->last_failed_timestamp);
+
+       if (archiver_stats->stat_reset_timestamp == 0)
+               nulls[6] = true;
+       else
+               values[6] = TimestampTzGetDatum(archiver_stats->stat_reset_timestamp);
+
+       /* Returns the record as Datum */
+       PG_RETURN_DATUM(HeapTupleGetDatum(
+                                                                  heap_form_tuple(tupdesc, values, nulls)));
+}