diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index c503636a885..85459d04b45 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -300,6 +300,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
+
+ pg_stat_wal_receiver>pg_stat_wal_receiver
+ Only one row, showing statistics about the WAL receiver from
+ that receiver's connected server.
+ See for details.
+
+
+
pg_stat_ssl>pg_stat_ssl
One row per connection (regular and replication), showing information about
@@ -833,6 +841,89 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
listed; no information is available about downstream standby servers.
+
+ pg_stat_wal_receiver View
+
+
+
+ Column
+ Type
+ Description
+
+
+
+
+
+ pid>
+ integer>
+ Process ID of the WAL receiver process
+
+
+ status>
+ text>
+ Activity status of the WAL receiver process
+
+
+ receive_start_lsn>
+ pg_lsn>
+ First transaction log position used when WAL receiver is
+ started
+
+
+ receive_start_tli>
+ integer>
+ First timeline number used when WAL receiver is started
+
+
+ received_lsn>
+ pg_lsn>
+ Last transaction log position already received and flushed to
+ disk, the initial value of this field being the first log position used
+ when WAL receiver is started
+
+
+ received_tli>
+ integer>
+ Timeline number of last transaction log position received and
+ flushed to disk, the initial value of this field being the timeline
+ number of the first log position used when WAL receiver is started
+
+
+
+ last_msg_send_time>
+ timestamp with time zone>
+ Send time of last message received from origin WAL sender
+
+
+ last_msg_receipt_time>
+ timestamp with time zone>
+ Receipt time of last message received from origin WAL sender
+
+
+ latest_end_lsn>
+ pg_lsn>
+ Last transaction log position reported to origin WAL sender
+
+
+ latest_end_time>
+ timestamp with time zone>
+ Time of last transaction log position reported to origin WAL sender
+
+
+ slot_name>
+ text>
+ Replication slot name used by this WAL receiver
+
+
+
+
+
+
+ The pg_stat_wal_receiver view will contain only
+ one row, showing statistics about the WAL receiver from that receiver's
+ connected server.
+
+
pg_stat_ssl View
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 2052afdc8d7..506a884e33f 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -662,6 +662,22 @@ CREATE VIEW pg_stat_replication AS
WHERE S.usesysid = U.oid AND
S.pid = W.pid;
+CREATE VIEW pg_stat_wal_receiver AS
+ SELECT
+ s.pid,
+ s.status,
+ s.receive_start_lsn,
+ s.receive_start_tli,
+ s.received_lsn,
+ s.received_tli,
+ s.last_msg_send_time,
+ s.last_msg_receipt_time,
+ s.latest_end_lsn,
+ s.latest_end_time,
+ s.slot_name
+ FROM pg_stat_get_wal_receiver() s
+ WHERE s.pid IS NOT NULL;
+
CREATE VIEW pg_stat_ssl AS
SELECT
S.pid,
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 81f152940d8..7b36e02faa9 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -46,9 +46,12 @@
#include
#include
+#include "access/htup_details.h"
#include "access/timeline.h"
#include "access/transam.h"
#include "access/xlog_internal.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
@@ -57,7 +60,9 @@
#include "storage/ipc.h"
#include "storage/pmsignal.h"
#include "storage/procarray.h"
+#include "utils/builtins.h"
#include "utils/guc.h"
+#include "utils/pg_lsn.h"
#include "utils/ps_status.h"
#include "utils/resowner.h"
#include "utils/timestamp.h"
@@ -1215,3 +1220,152 @@ ProcessWalSndrMessage(XLogRecPtr walEnd, TimestampTz sendTime)
pfree(receipttime);
}
}
+
+/*
+ * Return a string constant representing the state. This is used
+ * in system functions and views, and should *not* be translated.
+ */
+static const char *
+WalRcvGetStateString(WalRcvState state)
+{
+ switch (state)
+ {
+ case WALRCV_STOPPED:
+ return "stopped";
+ case WALRCV_STARTING:
+ return "starting";
+ case WALRCV_STREAMING:
+ return "streaming";
+ case WALRCV_WAITING:
+ return "waiting";
+ case WALRCV_RESTARTING:
+ return "restarting";
+ case WALRCV_STOPPING:
+ return "stopping";
+ }
+ return "UNKNOWN";
+}
+
+/*
+ * Returns activity of WAL receiver, including pid, state and xlog locations
+ * received from the WAL sender of another server.
+ */
+Datum
+pg_stat_get_wal_receiver(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_WAL_RECEIVER_COLS 11
+ TupleDesc tupdesc;
+ Datum values[PG_STAT_GET_WAL_RECEIVER_COLS];
+ bool nulls[PG_STAT_GET_WAL_RECEIVER_COLS];
+ WalRcvData *walrcv = WalRcv;
+ WalRcvState state;
+ XLogRecPtr receive_start_lsn;
+ TimeLineID receive_start_tli;
+ XLogRecPtr received_lsn;
+ TimeLineID received_tli;
+ TimestampTz last_send_time;
+ TimestampTz last_receipt_time;
+ XLogRecPtr latest_end_lsn;
+ TimestampTz latest_end_time;
+ char *slotname;
+
+ /* No WAL receiver, just return a tuple with NULL values */
+ if (walrcv->pid == 0)
+ PG_RETURN_NULL();
+
+ /* 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(PG_STAT_GET_WAL_RECEIVER_COLS, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pid",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "status",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "receive_start_lsn",
+ LSNOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "receive_start_tli",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 5, "received_lsn",
+ LSNOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 6, "received_tli",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 7, "last_msg_send_time",
+ TIMESTAMPTZOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 8, "last_msg_receipt_time",
+ TIMESTAMPTZOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 9, "latest_end_lsn",
+ LSNOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 10, "latest_end_time",
+ TIMESTAMPTZOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 11, "slot_name",
+ TEXTOID, -1, 0);
+
+ BlessTupleDesc(tupdesc);
+
+ /* Take a lock to ensure value consistency */
+ SpinLockAcquire(&walrcv->mutex);
+ state = walrcv->walRcvState;
+ receive_start_lsn = walrcv->receiveStart;
+ receive_start_tli = walrcv->receiveStartTLI;
+ received_lsn = walrcv->receivedUpto;
+ received_tli = walrcv->receivedTLI;
+ last_send_time = walrcv->lastMsgSendTime;
+ last_receipt_time = walrcv->lastMsgReceiptTime;
+ latest_end_lsn = walrcv->latestWalEnd;
+ latest_end_time = walrcv->latestWalEndTime;
+ slotname = pstrdup(walrcv->slotname);
+ SpinLockRelease(&walrcv->mutex);
+
+ /* Fetch values */
+ values[0] = Int32GetDatum(walrcv->pid);
+
+ if (!superuser())
+ {
+ /*
+ * Only superusers can see details. Other users only get the pid
+ * value to know whether it is a WAL receiver, but no details.
+ */
+ MemSet(&nulls[1], true, PG_STAT_GET_WAL_RECEIVER_COLS - 1);
+ }
+ else
+ {
+ values[1] = CStringGetTextDatum(WalRcvGetStateString(state));
+
+ if (XLogRecPtrIsInvalid(receive_start_lsn))
+ nulls[2] = true;
+ else
+ values[2] = LSNGetDatum(receive_start_lsn);
+ values[3] = Int32GetDatum(receive_start_tli);
+ if (XLogRecPtrIsInvalid(received_lsn))
+ nulls[4] = true;
+ else
+ values[4] = LSNGetDatum(received_lsn);
+ values[5] = Int32GetDatum(received_tli);
+ if (last_send_time == 0)
+ nulls[6] = true;
+ else
+ values[6] = TimestampTzGetDatum(last_send_time);
+ if (last_receipt_time == 0)
+ nulls[7] = true;
+ else
+ values[7] = TimestampTzGetDatum(last_receipt_time);
+ if (XLogRecPtrIsInvalid(latest_end_lsn))
+ nulls[8] = true;
+ else
+ values[8] = LSNGetDatum(latest_end_lsn);
+ if (latest_end_time == 0)
+ nulls[9] = true;
+ else
+ values[9] = TimestampTzGetDatum(latest_end_time);
+ if (*slotname == '\0')
+ nulls[10] = true;
+ else
+ values[10] = CStringGetTextDatum(slotname);
+ }
+
+ /* Returns the record as Datum */
+ PG_RETURN_DATUM(HeapTupleGetDatum(
+ heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 31a132ea984..eaa7a518918 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201601052
+#define CATALOG_VERSION_NO 201601071
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9250545d3b5..86b09a15eaa 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2787,6 +2787,8 @@ DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f
DESCR("statistics: information about currently active backends");
DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s r 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
DESCR("statistics: information about currently active replication");
+DATA(insert OID = 3317 ( pg_stat_get_wal_receiver PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{23,25,3220,23,3220,23,1184,1184,3220,1184,25}" "{o,o,o,o,o,o,o,o,o,o,o}" "{pid,status,receive_start_lsn,receive_start_tli,received_lsn,received_tli,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time,slot_name}" _null_ _null_ pg_stat_get_wal_receiver _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL receiver");
DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 23 "" _null_ _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
DESCR("statistics: current backend PID");
DATA(insert OID = 1937 ( pg_stat_get_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 23 "23" _null_ _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h
index db40d9d7603..6eacb095d1b 100644
--- a/src/include/replication/walreceiver.h
+++ b/src/include/replication/walreceiver.h
@@ -14,6 +14,7 @@
#include "access/xlog.h"
#include "access/xlogdefs.h"
+#include "fmgr.h"
#include "storage/latch.h"
#include "storage/spin.h"
#include "pgtime.h"
@@ -148,6 +149,7 @@ extern PGDLLIMPORT walrcv_disconnect_type walrcv_disconnect;
/* prototypes for functions in walreceiver.c */
extern void WalReceiverMain(void) pg_attribute_noreturn();
+extern Datum pg_stat_get_wal_receiver(PG_FUNCTION_ARGS);
/* prototypes for functions in walreceiverfuncs.c */
extern Size WalRcvShmemSize(void);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 80374e4d506..28b061f9d55 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1848,6 +1848,19 @@ pg_stat_user_tables| SELECT pg_stat_all_tables.relid,
pg_stat_all_tables.autoanalyze_count
FROM pg_stat_all_tables
WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text));
+pg_stat_wal_receiver| SELECT s.pid,
+ s.status,
+ s.receive_start_lsn,
+ s.receive_start_tli,
+ s.received_lsn,
+ s.received_tli,
+ s.last_msg_send_time,
+ s.last_msg_receipt_time,
+ s.latest_end_lsn,
+ s.latest_end_time,
+ s.slot_name
+ FROM pg_stat_get_wal_receiver() s(pid, status, receive_start_lsn, receive_start_tli, received_lsn, received_tli, last_msg_send_time, last_msg_receipt_time, latest_end_lsn, latest_end_time, slot_name)
+ WHERE (s.pid IS NOT NULL);
pg_stat_xact_all_tables| SELECT c.oid AS relid,
n.nspname AS schemaname,
c.relname,